Create & Init Project...

This commit is contained in:
2019-04-22 18:49:16 +08:00
commit fc4fa37393
25440 changed files with 4054998 additions and 0 deletions

View File

@@ -0,0 +1,68 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = [
"dao_test.go",
"databus_test.go",
"exp_test.go",
"grpc_test.go",
"hbase_test.go",
"redis_test.go",
],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/interface/main/history/conf:go_default_library",
"//app/interface/main/history/model:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"dao.go",
"databus.go",
"exp.go",
"grpc.go",
"hbase.go",
"redis.go",
],
importpath = "go-common/app/interface/main/history/dao/history",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/interface/main/history/conf:go_default_library",
"//app/interface/main/history/model:go_default_library",
"//app/service/openplatform/pgc-season/api/grpc/episode/v1:go_default_library",
"//library/cache:go_default_library",
"//library/cache/redis:go_default_library",
"//library/database/hbase.v2:go_default_library",
"//library/log:go_default_library",
"//library/net/metadata:go_default_library",
"//library/queue/databus:go_default_library",
"//vendor/github.com/tsuna/gohbase/hrpc:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,73 @@
package history
import (
"context"
"time"
"go-common/app/interface/main/history/conf"
eprpc "go-common/app/service/openplatform/pgc-season/api/grpc/episode/v1"
"go-common/library/cache"
"go-common/library/cache/redis"
"go-common/library/queue/databus"
"go-common/library/database/hbase.v2"
)
// Dao dao.
type Dao struct {
conf *conf.Config
info *hbase.Client
redis *redis.Pool
playPro *databus.Databus
merge *databus.Databus
experience *databus.Databus
proPub *databus.Databus
delChan *cache.Cache
expire int
epidGRPC eprpc.EpisodeClient
}
// New new history dao and return.
func New(c *conf.Config) (d *Dao) {
d = &Dao{
conf: c,
info: hbase.NewClient(c.Info.Config),
redis: redis.NewPool(c.Redis.Config),
playPro: databus.New(c.DataBus.PlayPro),
merge: databus.New(c.DataBus.Merge),
proPub: databus.New(c.DataBus.Pub),
experience: databus.New(c.DataBus.Experience),
delChan: cache.New(1, 1024),
expire: int(time.Duration(c.Redis.Expire) / time.Second),
}
var err error
if d.epidGRPC, err = eprpc.NewClient(nil); err != nil {
panic(err)
}
return
}
// Ping check connection success.
func (d *Dao) Ping(c context.Context) (err error) {
return d.PingRedis(c)
}
// Close close the redis and kafka resource.
func (d *Dao) Close() {
if d.redis != nil {
d.redis.Close()
}
if d.playPro != nil {
d.playPro.Close()
}
if d.merge != nil {
d.merge.Close()
}
if d.experience != nil {
d.experience.Close()
}
if d.proPub != nil {
d.proPub.Close()
}
}

View File

@@ -0,0 +1,33 @@
package history
import (
"flag"
"os"
"testing"
"go-common/app/interface/main/history/conf"
)
var (
d *Dao
)
func TestMain(m *testing.M) {
if os.Getenv("DEPLOY_ENV") != "" {
flag.Set("app_id", "main.community.history")
flag.Set("conf_token", "01423f5b752144440e01cb8ff4432e96")
flag.Set("tree_id", "2298")
flag.Set("conf_version", "docker-1")
flag.Set("deploy_env", "uat")
flag.Set("conf_host", "config.bilibili.co")
flag.Set("conf_path", "/tmp")
flag.Set("region", "sh")
flag.Set("zone", "sh001")
}
flag.Parse()
if err := conf.Init(); err != nil {
panic(err)
}
d = New(conf.Conf)
os.Exit(m.Run())
}

View File

@@ -0,0 +1,41 @@
package history
import (
"context"
"strconv"
"go-common/library/log"
)
// PlayPro send history to databus.
func (d *Dao) PlayPro(c context.Context, key string, msg interface{}) (err error) {
if err = d.playPro.Send(c, key, msg); err != nil {
log.Error("d.pub.Pub(%s,%v) error(%v)", key, msg, err)
}
return
}
// Merge send history to databus.
func (d *Dao) Merge(c context.Context, mid int64, msg interface{}) (err error) {
key := strconv.FormatInt(mid, 10)
if err = d.merge.Send(c, key, msg); err != nil {
log.Error("d.pub.Pub(%s,%v) error(%v)", key, msg, err)
}
return
}
// experiencePub send history to databus.
func (d *Dao) experiencePub(c context.Context, key string, msg interface{}) (err error) {
if err = d.experience.Send(c, key, msg); err != nil {
log.Error("d.pub.Pub(%s,%v) error(%v)", key, msg, err)
}
return
}
// ProPub send history to databus.
func (d *Dao) ProPub(c context.Context, key string, msg interface{}) (err error) {
if err = d.proPub.Send(c, key, msg); err != nil {
log.Error("d.ProPub.Pub(%s,%v) error(%v)", key, msg, err)
}
return
}

View File

@@ -0,0 +1,69 @@
package history
import (
"context"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestHistoryPlayPro(t *testing.T) {
convey.Convey("PlayPro", t, func(ctx convey.C) {
var (
c = context.Background()
key = "14771787"
msg = interface{}(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.PlayPro(c, key, msg)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestHistoryMerge(t *testing.T) {
convey.Convey("Merge", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(14771787)
msg = interface{}(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
d.Merge(c, mid, msg)
})
})
}
func TestHistoryexperiencePub(t *testing.T) {
convey.Convey("experiencePub", t, func(ctx convey.C) {
var (
c = context.Background()
key = "14771787"
msg = interface{}(14771787)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.experiencePub(c, key, msg)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestHistoryProPub(t *testing.T) {
convey.Convey("ProPub", t, func(ctx convey.C) {
var (
c = context.Background()
key = "14771787"
msg = interface{}(14771787)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.ProPub(c, key, msg)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}

View File

@@ -0,0 +1,72 @@
package history
import (
"context"
"strconv"
"time"
"go-common/library/cache/redis"
"go-common/library/log"
"go-common/library/net/metadata"
)
const (
_keyFirst = "his_f_"
_timeMonth = "0102"
_view = "view"
)
type experienceMsg struct {
Event string `json:"event"`
Mid int64 `json:"mid"`
IP string `json:"ip"`
TS int64 `json:"ts"`
}
// keyFirst return first key
func keyFirst(mid int64, t string) string {
return _keyFirst + strconv.FormatInt(mid%1000, 10) + "_" + t
}
// PushFirstQueue push first view record every day into kafka.
func (d *Dao) PushFirstQueue(c context.Context, mid, aid, now int64) (err error) {
var (
today = time.Unix(now, 0)
md = today.Format(_timeMonth)
key = keyFirst(mid, md)
conn = d.redis.Get(c)
)
defer conn.Close()
rp, err := redis.Int(conn.Do("SISMEMBER", key, mid))
if err != nil {
log.Error("conn.Do(SISMEMBER, %s, %d) error(%v)", key, mid, err)
err = nil
}
// if key exist , donot push to kafka
if rp > 0 {
return
}
midStr := strconv.FormatInt(mid, 10)
ex := &experienceMsg{
Event: _view,
Mid: mid,
IP: metadata.String(c, metadata.RemoteIP),
TS: now,
}
err = d.experiencePub(c, midStr, ex)
conn.Send("SADD", key, mid)
conn.Send("EXPIRE", key, 24*60*60)
if err = conn.Flush(); err != nil {
log.Error("conn.Flush error(%v)", err)
return
}
if _, err = conn.Receive(); err != nil {
log.Error("conn.Receive1() error(%v)", err)
return
}
if _, err = conn.Receive(); err != nil {
log.Error("conn.Receive2() error(%v)", err)
return
}
return
}

View File

@@ -0,0 +1,35 @@
package history
import (
"context"
"testing"
"time"
"github.com/smartystreets/goconvey/convey"
)
func TestHistorykeyFirst(t *testing.T) {
convey.Convey("keyFirst", t, func(ctx convey.C) {
var (
mid = int64(14771787)
no = "14771787"
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
keyFirst(mid, no)
})
})
}
func TestHistoryPushFirstQueue(t *testing.T) {
convey.Convey("PushFirstQueue", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(14771787)
aid = int64(1222)
now = time.Now().Unix()
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
d.PushFirstQueue(c, mid, aid, now)
})
})
}

View File

@@ -0,0 +1,64 @@
package history
import (
"context"
"go-common/app/interface/main/history/model"
epmdl "go-common/app/service/openplatform/pgc-season/api/grpc/episode/v1"
"go-common/library/log"
)
// Bangumis .
func (d *Dao) Bangumis(c context.Context, mid int64, epid []int64) (map[int64]*model.Bangumi, error) {
req := &epmdl.EpReq{}
for _, aid := range epid {
req.Epids = append(req.Epids, int32(aid))
}
resp, err := d.epidGRPC.Cards(c, req)
if err != nil {
log.Error("s.epidGRPC.Cards(%v, %v) error(%v)", c, req, err)
return nil, err
}
data := make(map[int64]*model.Bangumi)
for k, v := range resp.Cards {
data[int64(k)] = &model.Bangumi{
Epid: int64(v.EpisodeId),
Title: v.Title,
LongTitle: v.LongTitle,
EpisodeStatus: int(v.EpisodeStatus),
Cover: v.Cover,
Season: &model.Season{
ID: int64(v.Season.SeasonId),
Title: v.Season.Title,
SeasonStatus: int(v.Season.SeasonStatus),
IsFinish: int(v.Season.IsFinish),
TotalCount: v.Season.TotalCount,
NewestEpid: int64(v.Season.NewEpId),
NewestEpIndex: v.Season.NewEpIndex,
SeasonType: int(v.Season.SeasonType),
},
}
}
return data, nil
}
// BangumisByAids .
func (d *Dao) BangumisByAids(c context.Context, mid int64, aids []int64, realIP string) (map[int64]*model.BangumiSeason, error) {
req := &epmdl.EpAidReq{}
for _, aid := range aids {
req.Aids = append(req.Aids, int32(aid))
}
resp, err := d.epidGRPC.ListByAids(c, req)
if err != nil {
log.Error("s.epidGRPC.ListByAids(%v, %v) error(%v)", c, req, err)
return nil, err
}
data := make(map[int64]*model.BangumiSeason)
for _, v := range resp.Infos {
data[int64(v.Aid)] = &model.BangumiSeason{
ID: int64(v.SeasonId),
Epid: int64(v.EpisodeId),
}
}
return data, nil
}

View File

@@ -0,0 +1,39 @@
package history
import (
"context"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestHistoryBangumis(t *testing.T) {
convey.Convey("Bangumis", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(14771787)
epid = []int64{}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
d.Bangumis(c, mid, epid)
})
})
}
func TestHistoryBangumisByAids(t *testing.T) {
convey.Convey("BangumisByAids", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(14771787)
aids = []int64{11, 33}
realIP = ""
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1, err := d.BangumisByAids(c, mid, aids, realIP)
ctx.Convey("Then err should be nil.p1 should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}

View File

@@ -0,0 +1,286 @@
package history
import (
"bytes"
"context"
"crypto/md5"
"encoding/binary"
"encoding/json"
"errors"
"fmt"
"strconv"
"strings"
"time"
"go-common/app/interface/main/history/model"
"go-common/library/log"
"github.com/tsuna/gohbase/hrpc"
)
var (
tableInfo = "ugc:history"
family = "info"
familyB = []byte(family)
columnSW = "sw"
columnSWB = []byte(columnSW)
)
// hashRowKey create rowkey(md5(mid)[:2]+mid) for histroy by mid .
func hashRowKey(mid int64) string {
var bs = make([]byte, 8)
binary.LittleEndian.PutUint64(bs, uint64(mid))
rk := md5.Sum(bs)
return fmt.Sprintf("%x%d", rk[:2], mid)
}
func (d *Dao) column(aid int64, typ int8) string {
if typ < model.TypeArticle {
return strconv.FormatInt(aid, 10)
}
return fmt.Sprintf("%d_%d", aid, typ)
}
// Add add history list.
func (d *Dao) Add(ctx context.Context, mid int64, h *model.History) (err error) {
var (
valueByte []byte
column string
key = hashRowKey(mid)
fValues = make(map[string][]byte)
)
if h.Aid == 0 {
return
}
column = d.column(h.Aid, h.TP)
if valueByte, err = json.Marshal(h); err != nil {
log.Error("json.Marshal(%v) error(%v)", h, err)
return
}
fValues[column] = valueByte
values := map[string]map[string][]byte{family: fValues}
ctx, cancel := context.WithTimeout(ctx, time.Duration(d.conf.Info.WriteTimeout))
defer cancel()
if _, err = d.info.PutStr(ctx, tableInfo, key, values); err != nil {
log.Error("info.PutStr error(%v)", err)
}
return
}
// AddMap add history list.
func (d *Dao) AddMap(ctx context.Context, mid int64, hs map[int64]*model.History) (err error) {
var (
timeB []byte
column string
key = hashRowKey(mid)
fValues = make(map[string][]byte)
)
for _, h := range hs {
if h.Aid == 0 {
continue
}
// TODO typ and h.type is or not consistent .
column = d.column(h.Aid, h.TP)
if timeB, err = json.Marshal(h); err != nil {
log.Error("json.Marshal(%v) error(%v)", h, err)
continue
}
fValues[column] = timeB
}
values := map[string]map[string][]byte{family: fValues}
ctx, cancel := context.WithTimeout(ctx, time.Duration(d.conf.Info.WriteTimeout))
defer cancel()
if _, err = d.info.PutStr(ctx, tableInfo, key, values); err != nil {
log.Error("info.PutStr error(%v)", err)
}
return
}
// AidsMap get all Historys from hbase by aids.
func (d *Dao) AidsMap(ctx context.Context, mid int64, aids []int64) (his map[int64]*model.History, err error) {
key := hashRowKey(mid)
ctx, cancel := context.WithTimeout(ctx, time.Duration(d.conf.Info.ReadTimeout))
defer cancel()
var options []func(hrpc.Call) error
if len(aids) != 0 {
columns := make([]string, 0, len(aids))
for _, aid := range aids {
columns = append(columns, fmt.Sprintf("%d", aid))
}
options = append(options, hrpc.Families(map[string][]string{family: columns}))
}
if _, his, err = d.get(ctx, tableInfo, key, options...); err != nil {
log.Error("hbase get() error:%+v", err)
}
return
}
// Map get all Historys from hbase.
func (d *Dao) Map(ctx context.Context, mid int64) (his map[string]*model.History, err error) {
key := hashRowKey(mid)
ctx, cancel := context.WithTimeout(ctx, time.Duration(d.conf.Info.ReadTimeout))
defer cancel()
if his, _, err = d.get(ctx, tableInfo, key); err != nil {
log.Error("d.get() err:%+v", err)
}
return
}
// get hbase get op.
func (d *Dao) get(ctx context.Context, table, key string, options ...func(hrpc.Call) error) (his map[string]*model.History, av map[int64]*model.History, err error) {
var result *hrpc.Result
if result, err = d.info.GetStr(ctx, table, key, options...); err != nil {
log.Error("d.info.Get error(%v)", err)
return
}
if result == nil {
return
}
expire := time.Now().Unix() - 60*60*24*90 // NOTO hbase 90days validity.
delColumn := make(map[string][]byte)
his = make(map[string]*model.History, len(result.Cells))
av = make(map[int64]*model.History)
for _, c := range result.Cells {
if c != nil && bytes.Equal(c.Family, familyB) {
if bytes.Equal(c.Qualifier, columnSWB) {
continue
}
columns := strings.Split(string(c.Qualifier), "_")
if len(columns) == 0 {
continue
}
h := &model.History{}
h.Aid, err = strconv.ParseInt(columns[0], 10, 64)
if err != nil {
log.Error("strconv.ParseInt err(%v)", err)
continue
}
if err = json.Unmarshal(c.Value, h); err != nil {
log.Error("json.Unmarshal(%s) error(%v)", string(c.Value), err)
continue
}
if h.TP == model.TypeOffline {
h.TP = model.TypeUGC
}
if h.Unix == 0 { // live 默认时间 7月19号删除
h.Unix = int64(*(c.Timestamp)) / 1000
}
if h.Unix < expire {
delColumn[string(c.Qualifier)] = []byte{}
continue
}
his[d.column(h.Aid, h.TP)] = h
h.FillBusiness()
if h.TP < model.TypeArticle {
av[h.Aid] = h
}
}
}
if len(delColumn) > 0 {
d.delChan.Save(func() {
log.Warn("delete hbase key:%s", key)
d.delete(context.Background(), table, key, delColumn)
})
}
return
}
// Delete delete more history.
func (d *Dao) delete(ctx context.Context, table, key string, delColumn map[string][]byte) (err error) {
ctx, cancel := context.WithTimeout(ctx, time.Duration(d.conf.Info.WriteTimeout))
defer cancel()
values := map[string]map[string][]byte{family: delColumn}
if _, err = d.info.Delete(ctx, table, key, values); err != nil {
log.Error("info.Delete() delColumn:%+v,error(%v)", delColumn, err)
}
return
}
// DelAids delete more history.
func (d *Dao) DelAids(ctx context.Context, mid int64, aids []int64) (err error) {
columns := make(map[string][]byte, len(aids))
for _, aid := range aids {
columns[strconv.FormatInt(aid, 10)] = []byte{}
}
columns[strconv.FormatInt(mid, 10)] = []byte{}
columns[d.column(mid, model.TypeLive)] = []byte{}
ctx, cancel := context.WithTimeout(ctx, time.Duration(d.conf.Info.WriteTimeout))
defer cancel()
key := hashRowKey(mid)
values := map[string]map[string][]byte{family: columns}
if _, err = d.info.Delete(ctx, tableInfo, key, values); err != nil {
log.Error("info.Delete()len:%d,error(%v)", len(aids), err)
}
return
}
// Delete delete more history.
func (d *Dao) Delete(ctx context.Context, mid int64, his []*model.History) (err error) {
columns := make(map[string][]byte, len(his))
for _, h := range his {
columns[d.column(h.Aid, h.TP)] = []byte{}
}
ctx, cancel := context.WithTimeout(ctx, time.Duration(d.conf.Info.WriteTimeout))
defer cancel()
values := map[string]map[string][]byte{family: columns}
if _, err = d.info.Delete(ctx, tableInfo, hashRowKey(mid), values); err != nil {
log.Error("info.Delete()len:%d,error(%v)", len(his), err)
}
return
}
// Clear clear history.
func (d *Dao) Clear(ctx context.Context, mid int64) (err error) {
var sw int64
ctx, cancel := context.WithTimeout(ctx, time.Duration(d.conf.Info.ReadTimeout))
defer cancel()
if sw, err = d.InfoShadow(ctx, mid); err != nil {
return
}
key := hashRowKey(mid)
if _, err = d.info.Delete(ctx, tableInfo, key, nil); err != nil {
log.Error("info.Delete error(%v)", err)
}
if sw == model.ShadowOn {
d.SetInfoShadow(ctx, mid, sw)
}
return
}
// SetInfoShadow set the user switch to hbase.
func (d *Dao) SetInfoShadow(ctx context.Context, mid, value int64) (err error) {
var key = hashRowKey(mid)
ctx, cancel := context.WithTimeout(ctx, time.Duration(d.conf.Info.ReadTimeout))
defer cancel()
valueB := []byte(strconv.FormatInt(value, 10))
values := map[string]map[string][]byte{family: {columnSW: valueB}}
if _, err = d.info.PutStr(ctx, tableInfo, key, values); err != nil {
log.Error("info.Put error(%v)", err)
}
return
}
// InfoShadow return the user switch from hbase.
func (d *Dao) InfoShadow(ctx context.Context, mid int64) (sw int64, err error) {
var key = hashRowKey(mid)
ctx, cancel := context.WithTimeout(ctx, time.Duration(d.conf.Info.ReadTimeout))
defer cancel()
result, err := d.info.GetStr(ctx, tableInfo, key, hrpc.Families(map[string][]string{family: {columnSW}}))
if err != nil {
log.Error("d.info.Get error(%v)", err)
return
}
if result == nil {
err = errors.New("info sw data null")
return
}
for _, c := range result.Cells {
if c != nil && bytes.Equal(c.Family, familyB) {
if columnSW == string(c.Qualifier) {
sw, _ = strconv.ParseInt(string(c.Value), 10, 0)
return
}
}
}
return
}

View File

@@ -0,0 +1,214 @@
package history
import (
"context"
"testing"
"go-common/app/interface/main/history/model"
"github.com/smartystreets/goconvey/convey"
)
func TestHistoryhashRowKey(t *testing.T) {
convey.Convey("hashRowKey", t, func(ctx convey.C) {
var (
mid = int64(14771787)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1 := hashRowKey(mid)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestHistorycolumn(t *testing.T) {
convey.Convey("column", t, func(ctx convey.C) {
var (
aid = int64(14771787)
typ = int8(3)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1 := d.column(aid, typ)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestHistoryAdd(t *testing.T) {
convey.Convey("Add", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(14771787)
h = &model.History{Mid: 14771787, Aid: 32767458, TP: 3, Pro: 20, Unix: 1540976376, DT: 3}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.Add(c, mid, h)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestHistoryAddMap(t *testing.T) {
convey.Convey("AddMap", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(14771787)
h = &model.History{Mid: 14771787, Aid: 32767458, TP: 3, Pro: 20, Unix: 1540976376, DT: 3}
hs = map[int64]*model.History{14771787: h}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.AddMap(c, mid, hs)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestHistoryAidsMap(t *testing.T) {
convey.Convey("AidsMap", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(14771787)
aids = []int64{14771787}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
his, err := d.AidsMap(c, mid, aids)
ctx.Convey("Then err should be nil.his should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(his, convey.ShouldNotBeNil)
})
})
})
}
func TestHistoryMap(t *testing.T) {
convey.Convey("Map", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(14771787)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
his, err := d.Map(c, mid)
ctx.Convey("Then err should be nil.his should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(his, convey.ShouldNotBeNil)
})
})
})
}
func TestHistoryDelAids(t *testing.T) {
convey.Convey("DelAids", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(14771787)
aids = []int64{32767458}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.DelAids(c, mid, aids)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestHistoryget(t *testing.T) {
convey.Convey("delete", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(14771787)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
_, _, err := d.get(c, tableInfo, hashRowKey(mid))
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestHistorydelete(t *testing.T) {
convey.Convey("delete", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(14771787)
delColumn = map[string][]byte{"info:14771787": []byte{}}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.delete(c, tableInfo, hashRowKey(mid), delColumn)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestHistoryDelete(t *testing.T) {
convey.Convey("Delete", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(14771787)
h = &model.History{Mid: 14771787, Aid: 32767458, TP: 3, Pro: 20, Unix: 1540976376, DT: 3}
his = []*model.History{h}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.Delete(c, mid, his)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestHistoryClear(t *testing.T) {
convey.Convey("Clear", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(14771787)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.Clear(c, mid)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestHistorySetInfoShadow(t *testing.T) {
convey.Convey("SetInfoShadow", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(14771787)
value = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.SetInfoShadow(c, mid, value)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestHistoryInfoShadow(t *testing.T) {
convey.Convey("InfoShadow", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(14771787)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
sw, err := d.InfoShadow(c, mid)
ctx.Convey("Then err should be nil.sw should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(sw, convey.ShouldNotBeNil)
})
})
})
}

View File

@@ -0,0 +1,371 @@
package history
import (
"context"
"encoding/json"
"strconv"
"go-common/app/interface/main/history/model"
"go-common/library/cache/redis"
"go-common/library/log"
)
const (
_keyIndex = "i_" // mid -> score:time member:aid
_keyHistory = "h_" // mid -> hash(aid,progress)
_keySwitch = "s_" // mid -> bit(value)
_bucket = 1000 // bit bucket
)
// keyHistory return history key.
func keyHistory(mid int64) string {
return _keyHistory + strconv.FormatInt(mid, 10)
}
// keyIndex return history index key.
func keyIndex(mid int64) string {
return _keyIndex + strconv.FormatInt(mid, 10)
}
// keySwitch return Switch key.
func keySwitch(mid int64) string {
return _keySwitch + strconv.FormatInt(mid/_bucket, 10)
}
// ExpireIndexCache expire index cache.
func (d *Dao) ExpireIndexCache(c context.Context, mid int64) (bool, error) {
return d.expireCache(c, keyIndex(mid))
}
// ExpireCache expire the user State redis.
func (d *Dao) ExpireCache(c context.Context, mid int64) (bool, error) {
return d.expireCache(c, keyHistory(mid))
}
// ExpireToView expire toview by mid.
func (d *Dao) expireCache(c context.Context, key string) (ok bool, err error) {
conn := d.redis.Get(c)
if ok, err = redis.Bool(conn.Do("EXPIRE", key, d.expire)); err != nil {
log.Error("conn.Do(EXPIRE %s) error(%v)", key, err)
}
conn.Close()
return
}
// IndexCache get history from redis.
func (d *Dao) IndexCache(c context.Context, mid int64, start, end int) (aids []int64, err error) {
conn := d.redis.Get(c)
defer conn.Close()
values, err := redis.Values(conn.Do("ZREVRANGE", keyIndex(mid), start, end, "WITHSCORES"))
if err != nil {
log.Error("conn.Do(ZREVRANGE %v) error(%v)", keyIndex(mid), err)
return
}
if len(values) == 0 {
return
}
var aid, unix int64
for len(values) > 0 {
if values, err = redis.Scan(values, &aid, &unix); err != nil {
log.Error("redis.Scan(%v) error(%v)", values, err)
return
}
aids = append(aids, aid)
}
return
}
// IndexCacheByTime get aids from redis where score.
func (d *Dao) IndexCacheByTime(c context.Context, mid int64, start int64) (aids []int64, err error) {
conn := d.redis.Get(c)
defer conn.Close()
values, err := redis.Values(conn.Do("ZRANGEBYSCORE", keyIndex(mid), start, "INF", "WITHSCORES"))
if err != nil {
log.Error("conn.Do(ZRANGEBYSCORE %v) error(%v)", keyIndex(mid), err)
return
}
if len(values) == 0 {
return
}
var aid, unix int64
for len(values) > 0 {
if values, err = redis.Scan(values, &aid, &unix); err != nil {
log.Error("redis.Scan(%v) error(%v)", values, err)
return
}
aids = append(aids, aid)
}
return
}
// SetShadowCache set the user switch to redis.
func (d *Dao) SetShadowCache(c context.Context, mid, value int64) (err error) {
key := keySwitch(mid)
conn := d.redis.Get(c)
if _, err = conn.Do("HSET", key, mid%_bucket, value); err != nil {
log.Error("conn.Do(HSET %s,%d) error(%v)", key, value, err)
}
conn.Close()
return
}
// ShadowCache return user switch redis.
func (d *Dao) ShadowCache(c context.Context, mid int64) (value int64, err error) {
key := keySwitch(mid)
conn := d.redis.Get(c)
defer conn.Close()
if value, err = redis.Int64(conn.Do("HGET", key, mid%_bucket)); err != nil {
if err == redis.ErrNil {
return model.ShadowUnknown, nil
}
log.Error("conn.Do(HGET %s) error(%v)", key, err)
}
return
}
// CacheMap return the user State from redis.
func (d *Dao) CacheMap(c context.Context, mid int64) (amap map[int64]*model.History, err error) {
var (
values map[string]string
key = keyHistory(mid)
)
conn := d.redis.Get(c)
defer conn.Close()
if values, err = redis.StringMap(conn.Do("HGETALL", key)); err != nil {
log.Error("conn.Do(HGETALL %v) error(%v)", key, err)
if err == redis.ErrNil {
return nil, nil
}
return
}
amap = make(map[int64]*model.History)
for k, v := range values {
if v == "" {
continue
}
h := &model.History{}
if err = json.Unmarshal([]byte(v), h); err != nil {
log.Error("json.Unmarshal(%s) error(%v)", v, err)
err = nil
continue
}
if h.TP == model.TypeOffline {
h.TP = model.TypeUGC
}
h.Mid = mid
h.Aid, _ = strconv.ParseInt(k, 10, 0)
h.FillBusiness()
amap[h.Aid] = h
}
return
}
// Cache return the user State from redis.
func (d *Dao) Cache(c context.Context, mid int64, aids []int64) (amap map[int64]*model.History, miss []int64, err error) {
var (
values []string
aid int64
k int
key = keyHistory(mid)
args = []interface{}{key}
)
for _, aid = range aids {
args = append(args, aid)
}
conn := d.redis.Get(c)
defer conn.Close()
if values, err = redis.Strings(conn.Do("HMGET", args...)); err != nil {
log.Error("conn.Do(HMGET %v) error(%v)", args, err)
if err == redis.ErrNil {
return nil, nil, nil
}
return
}
amap = make(map[int64]*model.History, len(aids))
for k, aid = range aids {
if values[k] == "" {
miss = append(miss, aid)
continue
}
h := &model.History{}
if err = json.Unmarshal([]byte(values[k]), h); err != nil {
log.Error("json.Unmarshal(%s) error(%v)", values[k], err)
err = nil
continue
}
if h.TP == model.TypeOffline {
h.TP = model.TypeUGC
}
h.Mid = mid
h.Aid = aid
h.FillBusiness()
amap[aid] = h
}
return
}
// ClearCache clear the user State redis.
func (d *Dao) ClearCache(c context.Context, mid int64) (err error) {
var (
idxKey = keyIndex(mid)
key = keyHistory(mid)
conn = d.redis.Get(c)
)
defer conn.Close()
if err = conn.Send("DEL", idxKey); err != nil {
log.Error("conn.Send(DEL %s) error(%v)", idxKey, err)
return
}
if err = conn.Send("DEL", key); err != nil {
log.Error("conn.Send(DEL %s) error(%v)", key, err)
return
}
if err = conn.Flush(); err != nil {
log.Error("conn.Flush() error(%v)", err)
return
}
for i := 0; i < 2; i++ {
if _, err = conn.Receive(); err != nil {
log.Error("conn.Receive() error(%v)", err)
return
}
}
return
}
// DelCache delete the history redis.
func (d *Dao) DelCache(c context.Context, mid int64, aids []int64) (err error) {
var (
key1 = keyIndex(mid)
key2 = keyHistory(mid)
args1 = []interface{}{key1}
args2 = []interface{}{key2}
)
for _, aid := range aids {
args1 = append(args1, aid)
args2 = append(args2, aid)
}
conn := d.redis.Get(c)
defer conn.Close()
if err = conn.Send("ZREM", args1...); err != nil {
log.Error("conn.Send(ZREM %s,%v) error(%v)", key1, aids, err)
return
}
if err = conn.Send("HDEL", args2...); err != nil {
log.Error("conn.Send(HDEL %s,%v) error(%v)", key2, aids, err)
return
}
if err = conn.Flush(); err != nil {
log.Error("conn.Flush() error(%v)", err)
return
}
for i := 0; i < 2; i++ {
if _, err = conn.Receive(); err != nil {
log.Error("conn.Receive() error(%v)", err)
return
}
}
return
}
// AddCache add the user State to redis.
func (d *Dao) AddCache(c context.Context, mid int64, h *model.History) (err error) {
var (
b []byte
idxKey, key = keyIndex(mid), keyHistory(mid)
)
if b, err = json.Marshal(h); err != nil {
return
}
conn := d.redis.Get(c)
defer conn.Close()
if err = conn.Send("ZADD", idxKey, h.Unix, h.Aid); err != nil {
log.Error("conn.Send(ZADD %s,%d) error(%v)", key, h.Aid, err)
return
}
if err = conn.Send("HSET", key, h.Aid, string(b)); err != nil {
log.Error("conn.Send(HSET %s,%d) error(%v)", key, h.Aid, err)
return
}
if err = conn.Send("EXPIRE", idxKey, d.expire); err != nil {
log.Error("conn.Send(EXPIRE) error(%v)", err)
return
}
if err = conn.Send("EXPIRE", key, d.expire); err != nil {
log.Error("conn.Send(EXPIRE) error(%v)", err)
return
}
if err = conn.Flush(); err != nil {
log.Error("conn.Flush() error(%v)", err)
return
}
for i := 0; i < 2+2; i++ {
if _, err = conn.Receive(); err != nil {
log.Error("conn.Receive() error(%v)", err)
return
}
}
return
}
// AddCacheMap add the user State to redis.
func (d *Dao) AddCacheMap(c context.Context, mid int64, hm map[int64]*model.History) (err error) {
var (
b []byte
idxKey, key = keyIndex(mid), keyHistory(mid)
)
conn := d.redis.Get(c)
defer conn.Close()
for _, h := range hm {
if b, err = json.Marshal(h); err != nil {
continue
}
if err = conn.Send("ZADD", idxKey, h.Unix, h.Aid); err != nil {
log.Error("conn.Send(ZADD %s,%d) error(%v)", key, h.Aid, err)
continue
}
if err = conn.Send("HSET", key, h.Aid, string(b)); err != nil {
log.Error("conn.Send(HSET %s,%d) error(%v)", key, h.Aid, err)
continue
}
}
if err = conn.Send("EXPIRE", idxKey, d.expire); err != nil {
log.Error("conn.Send(EXPIRE) error(%v)", err)
return
}
if err = conn.Send("EXPIRE", key, d.expire); err != nil {
log.Error("conn.Send(EXPIRE) error(%v)", err)
return
}
if err = conn.Flush(); err != nil {
log.Error("conn.Flush() error(%v)", err)
return
}
for i := 0; i < len(hm)*2+2; i++ {
if _, err = conn.Receive(); err != nil {
log.Error("conn.Receive() error(%v)", err)
return
}
}
return
}
// TrimCache trim history.
func (d *Dao) TrimCache(c context.Context, mid int64, limit int) (err error) {
conn := d.redis.Get(c)
defer conn.Close()
aids, err := redis.Int64s(conn.Do("ZRANGE", keyIndex(mid), 0, -limit-1))
if err != nil {
log.Error("conn.Do(ZRANGE %v) error(%v)", keyIndex(mid), err)
return
}
if len(aids) == 0 {
return
}
return d.DelCache(c, mid, aids)
}
// PingRedis check redis connection
func (d *Dao) PingRedis(c context.Context) (err error) {
return
}

View File

@@ -0,0 +1,290 @@
package history
import (
"context"
"testing"
"go-common/app/interface/main/history/model"
"github.com/smartystreets/goconvey/convey"
)
func TestHistorykeyHistory(t *testing.T) {
convey.Convey("keyHistory", t, func(ctx convey.C) {
var (
mid = int64(14771787)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1 := keyHistory(mid)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestHistorykeyIndex(t *testing.T) {
convey.Convey("keyIndex", t, func(ctx convey.C) {
var (
mid = int64(14771787)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1 := keyIndex(mid)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestHistorykeySwitch(t *testing.T) {
convey.Convey("keySwitch", t, func(ctx convey.C) {
var (
mid = int64(14771787)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1 := keySwitch(mid)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestHistoryExpireIndexCache(t *testing.T) {
convey.Convey("ExpireIndexCache", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(14771787)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1, err := d.ExpireIndexCache(c, mid)
ctx.Convey("Then err should be nil.p1 should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestHistoryExpireCache(t *testing.T) {
convey.Convey("ExpireCache", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(14771787)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1, err := d.ExpireCache(c, mid)
ctx.Convey("Then err should be nil.p1 should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestHistoryexpireCache(t *testing.T) {
convey.Convey("expireCache", t, func(ctx convey.C) {
var (
c = context.Background()
key = ""
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
ok, err := d.expireCache(c, key)
ctx.Convey("Then err should be nil.ok should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(ok, convey.ShouldNotBeNil)
})
})
})
}
func TestHistoryIndexCache(t *testing.T) {
convey.Convey("IndexCache", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(14771787)
start = int(0)
end = int(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
_, err := d.IndexCache(c, mid, start, end)
ctx.Convey("Then err should be nil.aids should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestHistoryIndexCacheByTime(t *testing.T) {
convey.Convey("IndexCacheByTime", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(14771787)
start = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
_, err := d.IndexCacheByTime(c, mid, start)
ctx.Convey("Then err should be nil.aids should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestHistorySetShadowCache(t *testing.T) {
convey.Convey("SetShadowCache", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(14771787)
value = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.SetShadowCache(c, mid, value)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestHistoryShadowCache(t *testing.T) {
convey.Convey("ShadowCache", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(14771787)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
value, err := d.ShadowCache(c, mid)
ctx.Convey("Then err should be nil.value should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(value, convey.ShouldNotBeNil)
})
})
})
}
func TestHistoryCacheMap(t *testing.T) {
convey.Convey("CacheMap", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(14771787)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
amap, err := d.CacheMap(c, mid)
ctx.Convey("Then err should be nil.amap should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(amap, convey.ShouldNotBeNil)
})
})
})
}
func TestHistoryCache(t *testing.T) {
convey.Convey("Cache", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(14771787)
aids = []int64{14771787}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
_, _, err := d.Cache(c, mid, aids)
ctx.Convey("Then err should be nil.amap,miss should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestHistoryClearCache(t *testing.T) {
convey.Convey("ClearCache", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(14771787)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.ClearCache(c, mid)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestHistoryDelCache(t *testing.T) {
convey.Convey("DelCache", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(14771787)
aids = []int64{14771787}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.DelCache(c, mid, aids)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestHistoryAddCache(t *testing.T) {
convey.Convey("AddCache", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(14771787)
h = &model.History{}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.AddCache(c, mid, h)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestHistoryAddCacheMap(t *testing.T) {
convey.Convey("AddCacheMap", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(14771787)
hm map[int64]*model.History
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.AddCacheMap(c, mid, hm)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestHistoryTrimCache(t *testing.T) {
convey.Convey("TrimCache", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(14771787)
limit = int(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.TrimCache(c, mid, limit)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestHistoryPingRedis(t *testing.T) {
convey.Convey("PingRedis", t, func(ctx convey.C) {
var (
c = context.Background()
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.PingRedis(c)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}

View File

@@ -0,0 +1,57 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = [
"dao_test.go",
"hbase_test.go",
"redis_test.go",
],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/interface/main/history/conf:go_default_library",
"//app/interface/main/history/model:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"dao.go",
"hbase.go",
"redis.go",
],
importpath = "go-common/app/interface/main/history/dao/toview",
tags = ["automanaged"],
deps = [
"//app/interface/main/history/conf:go_default_library",
"//app/interface/main/history/model:go_default_library",
"//library/cache/redis:go_default_library",
"//library/database/hbase.v2:go_default_library",
"//library/log:go_default_library",
"//vendor/github.com/tsuna/gohbase/hrpc:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,41 @@
package toview
import (
"context"
"time"
"go-common/app/interface/main/history/conf"
"go-common/library/cache/redis"
"go-common/library/database/hbase.v2"
)
// Dao dao.
type Dao struct {
conf *conf.Config
info *hbase.Client
redis *redis.Pool
expire int
}
// New new history dao and return.
func New(c *conf.Config) (d *Dao) {
d = &Dao{
conf: c,
info: hbase.NewClient(c.Info.Config),
redis: redis.NewPool(c.Toview.Config),
expire: int(time.Duration(c.Toview.Expire) / time.Second),
}
return
}
// Ping check connection success.
func (d *Dao) Ping(c context.Context) (err error) {
return d.PingRedis(c)
}
// Close close the redis and kafka resource.
func (d *Dao) Close() {
if d.redis != nil {
d.redis.Close()
}
}

View File

@@ -0,0 +1,33 @@
package toview
import (
"flag"
"os"
"testing"
"go-common/app/interface/main/history/conf"
)
var (
d *Dao
)
func TestMain(m *testing.M) {
if os.Getenv("DEPLOY_ENV") != "" {
flag.Set("app_id", "main.community.history")
flag.Set("conf_token", "01423f5b752144440e01cb8ff4432e96")
flag.Set("tree_id", "2298")
flag.Set("conf_version", "docker-1")
flag.Set("deploy_env", "uat")
flag.Set("conf_host", "config.bilibili.co")
flag.Set("conf_path", "/tmp")
flag.Set("region", "sh")
flag.Set("zone", "sh001")
}
flag.Parse()
if err := conf.Init(); err != nil {
panic(err)
}
d = New(conf.Conf)
os.Exit(m.Run())
}

View File

@@ -0,0 +1,191 @@
package toview
import (
"bytes"
"context"
"crypto/md5"
"encoding/binary"
"fmt"
"strconv"
"time"
"go-common/app/interface/main/history/model"
"go-common/library/log"
"github.com/tsuna/gohbase/hrpc"
)
var (
tableInfo = "ugc:history_to_view"
familyAid = "aid"
familyAidB = []byte(familyAid)
)
// hashRowKey create rowkey(md5(mid)[:2]+mid) for histroy by mid .
func hashRowKey(mid int64) string {
var bs = make([]byte, 8)
binary.LittleEndian.PutUint64(bs, uint64(mid))
rk := md5.Sum(bs)
return fmt.Sprintf("%x%d", rk[:2], mid)
}
// Add add one toview.
func (d *Dao) Add(ctx context.Context, mid, aid, now int64) (err error) {
var (
timeB = make([]byte, 8)
key = hashRowKey(mid)
column = strconv.FormatInt(aid, 10)
)
ctx, cancel := context.WithTimeout(ctx, time.Duration(d.conf.Info.WriteTimeout))
defer cancel()
binary.BigEndian.PutUint64(timeB, uint64(now))
values := map[string]map[string][]byte{familyAid: map[string][]byte{column: timeB}}
if _, err = d.info.PutStr(ctx, tableInfo, key, values); err != nil {
log.Error("toview info.Put error(%v)", err)
}
return
}
// Adds add some toview.
func (d *Dao) Adds(ctx context.Context, mid int64, aids []int64, now int64) (err error) {
var (
timeB = make([]byte, 8)
key = hashRowKey(mid)
)
ctx, cancel := context.WithTimeout(ctx, time.Duration(d.conf.Info.WriteTimeout))
defer cancel()
binary.BigEndian.PutUint64(timeB, uint64(now))
aidValues := make(map[string][]byte, len(aids))
for _, aid := range aids {
aidValues[strconv.FormatInt(aid, 10)] = timeB
}
values := map[string]map[string][]byte{familyAid: aidValues}
if _, err = d.info.PutStr(ctx, tableInfo, key, values); err != nil {
log.Error("toview info.Put error(%v)", err)
}
return
}
// AddMap add some toview.
func (d *Dao) AddMap(ctx context.Context, mid int64, views map[int64]*model.ToView) (err error) {
var (
timeB = make([]byte, 8)
key = hashRowKey(mid)
)
ctx, cancel := context.WithTimeout(ctx, time.Duration(d.conf.Info.WriteTimeout))
defer cancel()
aidValues := make(map[string][]byte, len(views))
for _, v := range views {
binary.BigEndian.PutUint64(timeB, uint64(v.Unix))
aidValues[strconv.FormatInt(v.Aid, 10)] = timeB
}
values := map[string]map[string][]byte{familyAid: aidValues}
if _, err = d.info.PutStr(ctx, tableInfo, key, values); err != nil {
log.Error("toview info.Put error(%v)", err)
}
return
}
// ListInfo get all ToViews from hbase.
func (d *Dao) ListInfo(ctx context.Context, mid int64, aids []int64) (res []*model.ToView, err error) {
var (
result *hrpc.Result
key = hashRowKey(mid)
)
ctx, cancel := context.WithTimeout(ctx, time.Duration(d.conf.Info.ReadTimeout))
defer cancel()
var options []func(hrpc.Call) error
if len(aids) != 0 {
colunms := make([]string, 0, len(aids))
for _, aid := range aids {
colunms = append(colunms, fmt.Sprintf("%d", aid))
}
options = append(options, hrpc.Families(map[string][]string{familyAid: colunms}))
}
result, err = d.info.GetStr(ctx, tableInfo, key, options...)
if err != nil && result == nil {
log.Error("d.info.Get error(%v)", err)
return
}
res = make([]*model.ToView, 0)
for _, c := range result.Cells {
if c != nil && len(c.Value) == 8 && bytes.Equal(c.Family, familyAidB) {
aid, err := strconv.ParseInt(string(c.Qualifier), 10, 64)
if err != nil {
log.Error("strconv.ParseInt error(%v)", err)
continue
}
t := &model.ToView{Aid: aid}
t.Unix = int64(binary.BigEndian.Uint64(c.Value))
res = append(res, t)
}
}
return
}
// MapInfo get all ToViews from hbase.
func (d *Dao) MapInfo(ctx context.Context, mid int64, aids []int64) (res map[int64]*model.ToView, err error) {
var (
result *hrpc.Result
key = hashRowKey(mid)
)
ctx, cancel := context.WithTimeout(ctx, time.Duration(d.conf.Info.ReadTimeout))
defer cancel()
var options []func(hrpc.Call) error
if len(aids) != 0 {
colunms := make([]string, 0, len(aids))
for _, aid := range aids {
colunms = append(colunms, fmt.Sprintf("%d", aid))
}
options = append(options, hrpc.Families(map[string][]string{familyAid: colunms}))
}
result, err = d.info.GetStr(ctx, tableInfo, key, options...)
if err != nil {
log.Error("d.info.Get error(%v)", err)
return
}
res = make(map[int64]*model.ToView, len(aids))
if result == nil {
return
}
for _, c := range result.Cells {
if c != nil && len(c.Value) == 8 && bytes.Equal(c.Family, familyAidB) {
aid, err := strconv.ParseInt(string(c.Qualifier), 10, 64)
if err != nil {
log.Error("strconv.ParseInt error(%v)", err)
continue
}
t := &model.ToView{Aid: aid}
t.Unix = int64(binary.BigEndian.Uint64(c.Value))
res[aid] = t
}
}
return
}
// Del delete more toview.
func (d *Dao) Del(ctx context.Context, mid int64, aids []int64) (err error) {
key := hashRowKey(mid)
ctx, cancel := context.WithTimeout(ctx, time.Duration(d.conf.Info.WriteTimeout))
defer cancel()
columns := make(map[string][]byte, len(aids))
for _, aid := range aids {
columns[strconv.FormatInt(aid, 10)] = []byte{}
}
values := map[string]map[string][]byte{familyAid: columns}
if _, err = d.info.Delete(ctx, tableInfo, key, values); err != nil {
log.Error("toview info.Delete error(%v)", err)
}
return
}
// Clear clear ToView.
func (d *Dao) Clear(ctx context.Context, mid int64) (err error) {
key := hashRowKey(mid)
ctx, cancel := context.WithTimeout(ctx, time.Duration(d.conf.Info.WriteTimeout))
defer cancel()
if _, err = d.info.Delete(ctx, tableInfo, key, nil); err != nil {
log.Error("toview info.Delete error(%v)", err)
}
return
}

View File

@@ -0,0 +1,140 @@
package toview
import (
"context"
"testing"
"time"
"go-common/app/interface/main/history/model"
"github.com/smartystreets/goconvey/convey"
)
func TestToviewhashRowKey(t *testing.T) {
convey.Convey("hashRowKey", t, func(ctx convey.C) {
var (
mid = int64(14771787)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1 := hashRowKey(mid)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestToviewAdd(t *testing.T) {
convey.Convey("Add", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(14771787)
aid = int64(141787)
now = time.Now().Unix()
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.Add(c, mid, aid, now)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestToviewAdds(t *testing.T) {
convey.Convey("Adds", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(14771787)
aids = []int64{147717, 147787}
now = int64(14771787)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.Adds(c, mid, aids, now)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestToviewAddMap(t *testing.T) {
convey.Convey("AddMap", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(14771787)
views map[int64]*model.ToView
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.AddMap(c, mid, views)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestToviewListInfo(t *testing.T) {
convey.Convey("ListInfo", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(14771787)
aids = []int64{147717, 147787}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
res, err := d.ListInfo(c, mid, aids)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
})
}
func TestToviewMapInfo(t *testing.T) {
convey.Convey("MapInfo", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(14771787)
aids = []int64{147717, 147787}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
res, err := d.MapInfo(c, mid, aids)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
})
}
func TestToviewDel(t *testing.T) {
convey.Convey("Del", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(14771787)
aids = []int64{147717, 147787}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.Del(c, mid, aids)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestToviewClear(t *testing.T) {
convey.Convey("Clear", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(14771787)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.Clear(c, mid)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}

View File

@@ -0,0 +1,160 @@
package toview
import (
"context"
"strconv"
"go-common/app/interface/main/history/model"
"go-common/library/cache/redis"
"go-common/library/log"
)
const _key = "v_" // mid -> score:time member:aid
// keyToView return key string
func key(mid int64) string {
return _key + strconv.FormatInt(mid, 10)
}
// Expire expire toview by mid.
func (d *Dao) Expire(c context.Context, mid int64) (ok bool, err error) {
conn := d.redis.Get(c)
if ok, err = redis.Bool(conn.Do("EXPIRE", key(mid), d.expire)); err != nil {
log.Error("conn.Do(EXPIRE, %s) error(%v)", key(mid), err)
}
conn.Close()
return
}
// Cache return the user all toview from redis.
func (d *Dao) Cache(c context.Context, mid int64, start, end int) (res []*model.ToView, err error) {
conn := d.redis.Get(c)
defer conn.Close()
values, err := redis.Values(conn.Do("ZREVRANGE", key(mid), start, end, "WITHSCORES"))
if err != nil {
log.Error("dao.Do(ZREVRANGE %v) error(%v)", key(mid), err)
return
}
if len(values) == 0 {
return
}
res = make([]*model.ToView, 0, len(values)/2)
for len(values) > 0 {
t := &model.ToView{}
if values, err = redis.Scan(values, &t.Aid, &t.Unix); err != nil {
log.Error("redis.Scan(%v) error(%v)", values, err)
return
}
res = append(res, t)
}
return
}
// CacheMap return the user all toview map from redis.
func (d *Dao) CacheMap(c context.Context, mid int64) (res map[int64]*model.ToView, err error) {
conn := d.redis.Get(c)
defer conn.Close()
values, err := redis.Values(conn.Do("ZREVRANGE", key(mid), 0, -1, "WITHSCORES"))
if err != nil {
log.Error("dao.Do(ZREVRANGE %v) error(%v)", key(mid), err)
return
}
if len(values) == 0 {
return
}
res = make(map[int64]*model.ToView, len(values)/2)
for len(values) > 0 {
t := &model.ToView{}
if values, err = redis.Scan(values, &t.Aid, &t.Unix); err != nil {
log.Error("redis.Scan(%v) error(%v)", values, err)
return
}
res[t.Aid] = t
}
return
}
// CntCache return the user toview count from redis.
func (d *Dao) CntCache(c context.Context, mid int64) (count int, err error) {
conn := d.redis.Get(c)
if count, err = redis.Int(conn.Do("ZCARD", key(mid))); err != nil {
log.Error("dao.Do(ZCARD,%s) err(%v)", key(mid), err)
}
conn.Close()
return
}
// ClearCache delete the user toview redis.
func (d *Dao) ClearCache(c context.Context, mid int64) (err error) {
conn := d.redis.Get(c)
if _, err = conn.Do("DEL", key(mid)); err != nil {
log.Error("conn.Do(DEL %s) error(%v)", key(mid), err)
}
conn.Close()
return
}
// DelCaches delete the user toview redis.
func (d *Dao) DelCaches(c context.Context, mid int64, aids []int64) (err error) {
conn := d.redis.Get(c)
defer conn.Close()
for _, aid := range aids {
if err = conn.Send("ZREM", key(mid), aid); err != nil {
log.Error("conn.Send(ZREM %s,%d) error(%v)", key(mid), aid, err)
return
}
}
if err = conn.Flush(); err != nil {
log.Error("conn.Flush() error(%v)", err)
return
}
for i := 0; i < len(aids); i++ {
if _, err = conn.Receive(); err != nil {
log.Error("conn.Receive() error(%v)", err)
return
}
}
return
}
// AddCache add user toview to redis.
func (d *Dao) AddCache(c context.Context, mid, aid, now int64) error {
return d.addCache(c, key(mid), []*model.ToView{&model.ToView{Aid: aid, Unix: now}})
}
// AddCacheList add user toview to redis.
func (d *Dao) AddCacheList(c context.Context, mid int64, views []*model.ToView) error {
return d.addCache(c, key(mid), views)
}
// addCache add user toview to redis.
func (d *Dao) addCache(c context.Context, key string, views []*model.ToView) (err error) {
conn := d.redis.Get(c)
defer conn.Close()
for _, v := range views {
if err = conn.Send("ZADD", key, v.Unix, v.Aid); err != nil {
log.Error("conn.Send(ZREM %s,%d) error(%v)", key, v.Aid, err)
return
}
}
if err = conn.Send("EXPIRE", key, d.expire); err != nil {
log.Error("conn.Send(EXPIRE) error(%v)", err)
return
}
if err = conn.Flush(); err != nil {
log.Error("conn.Flush() error(%v)", err)
return
}
for i := 0; i < len(views)+1; i++ {
if _, err = conn.Receive(); err != nil {
log.Error("conn.Receive() error(%v)", err)
return
}
}
return
}
// PingRedis check redis connection
func (d *Dao) PingRedis(c context.Context) (err error) {
return
}

View File

@@ -0,0 +1,184 @@
package toview
import (
"context"
"testing"
"time"
"go-common/app/interface/main/history/model"
"github.com/smartystreets/goconvey/convey"
)
func TestToviewkey(t *testing.T) {
convey.Convey("key", t, func(ctx convey.C) {
var (
mid = int64(14771787)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1 := key(mid)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestToviewExpire(t *testing.T) {
convey.Convey("Expire", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(14771787)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
ok, err := d.Expire(c, mid)
ctx.Convey("Then err should be nil.ok should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(ok, convey.ShouldNotBeNil)
})
})
})
}
func TestToviewCache(t *testing.T) {
convey.Convey("Cache", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(14771787)
start = int(1)
end = int(2)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
_, err := d.Cache(c, mid, start, end)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestToviewCacheMap(t *testing.T) {
convey.Convey("CacheMap", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(14771787)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
res, err := d.CacheMap(c, mid)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
})
}
func TestToviewCntCache(t *testing.T) {
convey.Convey("CntCache", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(14771787)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
count, err := d.CntCache(c, mid)
ctx.Convey("Then err should be nil.count should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(count, convey.ShouldNotBeNil)
})
})
})
}
func TestToviewClearCache(t *testing.T) {
convey.Convey("ClearCache", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(14771787)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.ClearCache(c, mid)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestToviewDelCaches(t *testing.T) {
convey.Convey("DelCaches", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(14771787)
aids = []int64{}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.DelCaches(c, mid, aids)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestToviewAddCache(t *testing.T) {
convey.Convey("AddCache", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(14771787)
aid = int64(14771787)
now = time.Now().Unix()
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.AddCache(c, mid, aid, now)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestToviewAddCacheList(t *testing.T) {
convey.Convey("AddCacheList", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(14771787)
views = []*model.ToView{}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.AddCacheList(c, mid, views)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestToviewaddCache(t *testing.T) {
convey.Convey("addCache", t, func(ctx convey.C) {
var (
c = context.Background()
key = ""
views = []*model.ToView{{Aid: 1477, Unix: 11}}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.addCache(c, key, views)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestToviewPingRedis(t *testing.T) {
convey.Convey("PingRedis", t, func(ctx convey.C) {
var (
c = context.Background()
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.PingRedis(c)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}