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",
"hbase_test.go",
"mysql_test.go",
"redis_test.go",
],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/interface/main/history/model:go_default_library",
"//app/job/main/history/conf:go_default_library",
"//app/service/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",
"mysql.go",
"redis.go",
],
importpath = "go-common/app/job/main/history/dao",
tags = ["automanaged"],
deps = [
"//app/interface/main/history/model:go_default_library",
"//app/job/main/history/conf:go_default_library",
"//app/service/main/history/model:go_default_library",
"//library/cache/redis:go_default_library",
"//library/database/hbase.v2:go_default_library",
"//library/database/tidb:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster: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"],
)
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)

View File

@@ -0,0 +1,117 @@
package dao
import (
"context"
"errors"
"fmt"
"net/url"
"time"
"go-common/app/job/main/history/conf"
"go-common/app/service/main/history/model"
"go-common/library/cache/redis"
"go-common/library/database/tidb"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/database/hbase.v2"
)
var errFlushRequest = errors.New("error flush history to store")
// Dao dao.
type Dao struct {
conf *conf.Config
HTTPClient *bm.Client
URL string
info *hbase.Client
redis *redis.Pool
db *tidb.DB
longDB *tidb.DB
insertStmt *tidb.Stmts
businessesStmt *tidb.Stmts
allHisStmt *tidb.Stmts
delUserStmt *tidb.Stmts
BusinessesMap map[int64]*model.Business
BusinessNames map[string]*model.Business
}
// New new history dao and return.
func New(c *conf.Config) (dao *Dao) {
dao = &Dao{
conf: c,
redis: redis.NewPool(c.Redis),
HTTPClient: bm.NewClient(c.Job.Client),
URL: c.Job.URL,
info: hbase.NewClient(c.Info.Config),
db: tidb.NewTiDB(c.TiDB),
longDB: tidb.NewTiDB(c.LongTiDB),
}
dao.businessesStmt = dao.db.Prepared(_businessesSQL)
dao.insertStmt = dao.db.Prepared(_addHistorySQL)
dao.allHisStmt = dao.db.Prepared(_allHisSQL)
dao.delUserStmt = dao.db.Prepared(_delUserHisSQL)
dao.loadBusiness()
go dao.loadBusinessproc()
return
}
// Flush flush history to store by mids.
func (d *Dao) Flush(c context.Context, mids string, stime int64) (err error) {
params := url.Values{}
params.Set("mids", mids)
params.Set("time", fmt.Sprintf("%d", stime))
var res = &struct {
Code int `json:"code"`
Msg string `json:"message"`
}{}
if err = d.HTTPClient.Post(c, d.URL, "", params, res); err != nil {
log.Error("d.HTTPClient.Post(%s?%s) error(%v)", d.URL, params.Encode(), err)
return
}
if res.Code != 0 {
log.Error("d.HTTPClient.Post(%s?%s) code:%d msg:%s", d.URL, params.Encode(), res.Code, res.Msg)
err = errFlushRequest
return
}
return
}
// Ping check connection success.
func (d *Dao) Ping(c context.Context) (err error) {
return
}
// Close close the redis and kafka resource.
func (d *Dao) Close() {
d.redis.Close()
d.db.Close()
d.longDB.Close()
}
func (d *Dao) loadBusiness() {
var business []*model.Business
var err error
businessMap := make(map[string]*model.Business)
businessIDMap := make(map[int64]*model.Business)
for {
if business, err = d.Businesses(context.TODO()); err != nil {
time.Sleep(time.Second)
continue
}
for _, b := range business {
businessMap[b.Name] = b
businessIDMap[b.ID] = b
}
d.BusinessNames = businessMap
d.BusinessesMap = businessIDMap
return
}
}
func (d *Dao) loadBusinessproc() {
for {
time.Sleep(time.Minute * 5)
d.loadBusiness()
}
}

View File

@@ -0,0 +1,34 @@
package dao
import (
"flag"
"go-common/app/job/main/history/conf"
"os"
"testing"
)
var (
d *Dao
)
func TestMain(m *testing.M) {
if os.Getenv("DEPLOY_ENV") != "" {
flag.Set("app_id", "main.community.history-job")
flag.Set("conf_token", "8cf9e76766a95b96d18ab7c5b621374d")
flag.Set("tree_id", "2297")
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")
} else {
flag.Set("conf", "../cmd/history-job.toml")
}
flag.Parse()
if err := conf.Init(); err != nil {
panic(err)
}
d = New(conf.Conf)
os.Exit(m.Run())
}

View File

@@ -0,0 +1,54 @@
package dao
import (
"context"
"crypto/md5"
"encoding/binary"
"encoding/json"
"fmt"
"strconv"
"time"
"go-common/app/interface/main/history/model"
"go-common/library/log"
)
var (
tableInfo = "ugc:history"
family = "info"
)
// 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, h *model.History) error {
valueByte, err := json.Marshal(h)
if err != nil {
log.Error("json.Marshal(%v) error(%v)", h, err)
return err
}
fValues := make(map[string][]byte)
column := d.column(h.Aid, h.TP)
fValues[column] = valueByte
key := hashRowKey(h.Mid)
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 nil
}

View File

@@ -0,0 +1,52 @@
package dao
import (
"context"
"go-common/app/interface/main/history/model"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestDaohashRowKey(t *testing.T) {
convey.Convey("hashRowKey", t, func(ctx convey.C) {
var (
mid = int64(14771787)
)
ctx.Convey("When everything gose 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 TestDaocolumn(t *testing.T) {
convey.Convey("column", t, func(ctx convey.C) {
var (
aid = int64(14771787)
typ = int8(3)
)
ctx.Convey("When everything gose 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 TestDaoAdd(t *testing.T) {
convey.Convey("Add", t, func(ctx convey.C) {
var (
h = &model.History{Mid: 14771787, Aid: 14771787}
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
err := d.Add(context.Background(), h)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}

View File

@@ -0,0 +1,124 @@
package dao
import (
"context"
"database/sql"
"time"
"go-common/app/service/main/history/model"
"go-common/library/database/tidb"
"go-common/library/log"
)
var (
_businessesSQL = "SELECT id, name, ttl FROM business"
_addHistorySQL = "INSERT INTO histories(mid, kid, business_id, aid, sid, epid, sub_type, cid, device, progress, view_at) VALUES(?,?,?,?,?,?,?,?,?,?,?)" +
"ON DUPLICATE KEY UPDATE aid =?, sid=?, epid=?, sub_type=?, cid=?, device=?, progress=?, view_at=?"
_deleteSQL = "DELETE FROM histories WHERE business_id = ? AND mtime >= ? AND mtime < ? LIMIT ?"
_allHisSQL = "SELECT mtime FROM histories WHERE mid = ? AND business_id = ? ORDER BY mtime desc"
_earlyHistorySQL = "SELECT mtime FROM histories WHERE business_id = ? ORDER BY mtime LIMIT 1"
_delUserHisSQL = "DELETE FROM histories WHERE mid = ? AND mtime < ? and business_id = ?"
)
// Businesses business
func (d *Dao) Businesses(c context.Context) (res []*model.Business, err error) {
var rows *tidb.Rows
if rows, err = d.businessesStmt.Query(c); err != nil {
log.Error("db.businessesStmt.Query error(%v)", err)
return
}
defer rows.Close()
for rows.Next() {
b := &model.Business{}
if err = rows.Scan(&b.ID, &b.Name, &b.TTL); err != nil {
log.Error("rows.Business.Scan error(%v)", err)
return
}
res = append(res, b)
}
err = rows.Err()
return
}
// DeleteHistories delete histories
func (d *Dao) DeleteHistories(c context.Context, bid int64, beginTime, endTime time.Time) (rows int64, err error) {
var res sql.Result
begin := time.Now()
if res, err = d.longDB.Exec(c, _deleteSQL, bid, beginTime, endTime, d.conf.Job.DeleteLimit); err != nil {
log.Error("DeleteHistories(%v %v %v) err: %v", bid, beginTime, endTime, err)
return
}
rows, err = res.RowsAffected()
log.Info("clean business histories bid: %v begin: %v end: %v rows: %v, time: %v", bid, beginTime, endTime, rows, time.Since(begin))
return
}
// AddHistories add histories to db
func (d *Dao) AddHistories(c context.Context, hs []*model.History) (err error) {
if len(hs) == 0 {
return
}
var tx *tidb.Tx
if tx, err = d.db.Begin(c); err != nil {
log.Error("tx.BeginTran() error(%v)", err)
return
}
for _, h := range hs {
if _, err = tx.Stmts(d.insertStmt).Exec(c, h.Mid, h.Kid, h.BusinessID, h.Aid, h.Sid, h.Epid, h.SubType, h.Cid, h.Device, h.Progress, h.ViewAt,
h.Aid, h.Sid, h.Epid, h.SubType, h.Cid, h.Device, h.Progress, h.ViewAt); err != nil {
log.Error("addHistories exec err mid: %v err: %+v", h.Mid, err)
tx.Rollback()
return
}
}
if err = tx.Commit(); err != nil {
log.Error("add histories commit(%+v) err: %v", hs, err)
return
}
log.Infov(c, log.D{Key: "log", Value: "addHistories db"}, log.D{Key: "len", Value: len(hs)})
return
}
// DeleteUserHistories .
func (d *Dao) DeleteUserHistories(c context.Context, mid, bid int64, t time.Time) (rows int64, err error) {
var res sql.Result
if res, err = d.delUserStmt.Exec(c, mid, t, bid); err != nil {
log.Error("DeleteUserHistories(%v %v) err: %v", bid, t, err)
return
}
rows, err = res.RowsAffected()
return
}
// UserHistories .
func (d *Dao) UserHistories(c context.Context, mid, businessID int64) (res []time.Time, err error) {
var rows *tidb.Rows
if rows, err = d.allHisStmt.Query(c, mid, businessID); err != nil {
log.Error("db.UserHistories.Query error(%v)", err)
return
}
defer rows.Close()
for rows.Next() {
var t time.Time
if err = rows.Scan(&t); err != nil {
log.Error("rows.UserHistories.Scan error(%v)", err)
return
}
res = append(res, t)
}
err = rows.Err()
return
}
// EarlyHistory .
func (d *Dao) EarlyHistory(c context.Context, businessID int64) (res time.Time, err error) {
if err = d.longDB.QueryRow(c, _earlyHistorySQL, businessID).Scan(&res); err != nil {
if err == tidb.ErrNoRows {
res = time.Now()
err = nil
return
}
log.Error("db.EarlyHistory.Query error(%v)", err)
}
return
}

View File

@@ -0,0 +1,108 @@
package dao
import (
"context"
"go-common/app/service/main/history/model"
"testing"
"time"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoBusinesses(t *testing.T) {
convey.Convey("Businesses", t, func(ctx convey.C) {
var (
c = context.Background()
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
res, err := d.Businesses(c)
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 TestDaoDeleteHistories(t *testing.T) {
convey.Convey("DeleteHistories", t, func(ctx convey.C) {
var (
c = context.Background()
bid = int64(14771787)
beginTime = time.Now()
endTime = time.Now()
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
rows, err := d.DeleteHistories(c, bid, beginTime, endTime)
ctx.Convey("Then err should be nil.rows should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(rows, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoAddHistories(t *testing.T) {
convey.Convey("AddHistories", t, func(ctx convey.C) {
var (
c = context.Background()
hs = []*model.History{}
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
err := d.AddHistories(c, hs)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestDaoDeleteUserHistories(t *testing.T) {
convey.Convey("DeleteUserHistories", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(14771787)
bid = int64(14771787)
no = time.Now()
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
rows, err := d.DeleteUserHistories(c, mid, bid, no)
ctx.Convey("Then err should be nil.rows should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(rows, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoUserHistories(t *testing.T) {
convey.Convey("UserHistories", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(14771787)
businessID = int64(3)
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
_, err := d.UserHistories(c, mid, businessID)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestDaoEarlyHistory(t *testing.T) {
convey.Convey("EarlyHistory", t, func(ctx convey.C) {
var (
c = context.Background()
businessID = int64(3)
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
res, err := d.EarlyHistory(c, businessID)
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)
})
})
})
}

View File

@@ -0,0 +1,138 @@
package dao
import (
"context"
"encoding/json"
"fmt"
"time"
"go-common/app/service/main/history/model"
"go-common/library/cache/redis"
"go-common/library/log"
)
const _deleteDuration = 3600 * 12
// keyIndex return history index key.
func keyIndex(business string, mid int64) string {
return fmt.Sprintf("i_%d_%s", mid, business)
}
// keyHistory return history key.
func keyHistory(business string, mid int64) string {
return fmt.Sprintf("h_%d_%s", mid, business)
}
// HistoriesCache return the user histories from redis.
func (d *Dao) HistoriesCache(c context.Context, merges []*model.Merge) (res []*model.History, err error) {
conn := d.redis.Get(c)
defer conn.Close()
for _, merge := range merges {
key := keyHistory(d.BusinessesMap[merge.Bid].Name, merge.Mid)
if err = conn.Send("HGET", key, merge.Kid); err != nil {
log.Error("conn.Send(HGET %v %v) error(%v)", key, merge.Kid, err)
return
}
}
if err = conn.Flush(); err != nil {
log.Error("conn.Flush() error(%v)", err)
return
}
for i := 0; i < len(merges); i++ {
var value []byte
if value, err = redis.Bytes(conn.Receive()); err != nil {
if err == redis.ErrNil {
err = nil
continue
}
log.Error("conn.Receive error(%v)", err)
return
}
h := &model.History{}
if err = json.Unmarshal(value, h); err != nil {
log.Error("json.Unmarshal(%s) error(%v)", value, err)
err = nil
continue
}
h.BusinessID = d.BusinessNames[h.Business].ID
res = append(res, h)
}
return
}
// TrimCache trim history.
func (d *Dao) TrimCache(c context.Context, business string, mid int64, limit int) (err error) {
conn := d.redis.Get(c)
defer conn.Close()
aids, err := redis.Int64s(conn.Do("ZRANGE", keyIndex(business, mid), 0, -limit-1))
if err != nil {
log.Error("conn.Do(ZRANGE %v) error(%v)", keyIndex(business, mid), err)
return
}
if len(aids) == 0 {
return
}
return d.DelCache(c, business, mid, aids)
}
// DelCache delete the history redis.
func (d *Dao) DelCache(c context.Context, business string, mid int64, aids []int64) (err error) {
var (
key1 = keyIndex(business, mid)
key2 = keyHistory(business, 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
}
// DelLock delete proc lock
func (d *Dao) DelLock(c context.Context) (ok bool, err error) {
conn := d.redis.Get(c)
defer conn.Close()
key := "his_job_del_proc"
if err = conn.Send("SETNX", key, time.Now().Unix()); err != nil {
log.Error("DelLock conn.SETNX() error(%v)", err)
return
}
if err = conn.Send("EXPIRE", key, _deleteDuration); err != nil {
log.Error("DelLock conn.Expire() error(%v)", err)
return
}
if err = conn.Flush(); err != nil {
log.Error("DelLock conn.Flush() error(%v)", err)
return
}
if ok, err = redis.Bool(conn.Receive()); err != nil {
log.Error("conn.Receive() error(%v)", err)
return
}
if _, err = conn.Receive(); err != nil {
log.Error("conn.Receive() error(%v)", err)
}
return
}

View File

@@ -0,0 +1,107 @@
package dao
import (
"context"
"go-common/app/service/main/history/model"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestDaokeyIndex(t *testing.T) {
convey.Convey("keyIndex", t, func(ctx convey.C) {
var (
business = ""
mid = int64(14771787)
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
p1 := keyIndex(business, mid)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestDaokeyHistory(t *testing.T) {
convey.Convey("keyHistory", t, func(ctx convey.C) {
var (
business = "archive"
mid = int64(14771787)
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
p1 := keyHistory(business, mid)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoHistoriesCache(t *testing.T) {
convey.Convey("HistoriesCache", t, func(ctx convey.C) {
var (
c = context.Background()
merges = []*model.Merge{}
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
_, err := d.HistoriesCache(c, merges)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestDaoTrimCache(t *testing.T) {
convey.Convey("TrimCache", t, func(ctx convey.C) {
var (
c = context.Background()
business = ""
mid = int64(14771787)
limit = int(10)
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
err := d.TrimCache(c, business, mid, limit)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestDaoDelCache(t *testing.T) {
convey.Convey("DelCache", t, func(ctx convey.C) {
var (
c = context.Background()
business = ""
mid = int64(14771787)
aids = []int64{}
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
err := d.DelCache(c, business, mid, aids)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
if err != nil {
ctx.So(err, convey.ShouldNotBeNil)
} else {
ctx.So(err, convey.ShouldBeNil)
}
})
})
})
}
func TestDaoDelLock(t *testing.T) {
convey.Convey("DelLock", t, func(ctx convey.C) {
var (
c = context.Background()
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
ok, err := d.DelLock(c)
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)
})
})
})
}