abtest.go 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. package abtest
  2. import (
  3. "context"
  4. "time"
  5. "go-common/app/interface/main/app-resource/conf"
  6. expdao "go-common/app/interface/main/app-resource/dao/abtest"
  7. "go-common/app/interface/main/app-resource/model/experiment"
  8. "go-common/app/service/main/resource/model"
  9. "go-common/library/log"
  10. farm "github.com/dgryski/go-farm"
  11. )
  12. var (
  13. _emptyExperiment = []*experiment.Experiment{}
  14. _defaultExperiment = map[int8][]*experiment.Experiment{
  15. model.PlatAndroid: []*experiment.Experiment{
  16. &experiment.Experiment{
  17. ID: 10,
  18. Name: "默认值",
  19. Strategy: "default_value",
  20. Desc: "默认值为不匹配处理",
  21. TrafficGroup: "0",
  22. },
  23. },
  24. }
  25. )
  26. type Service struct {
  27. dao *expdao.Dao
  28. // tick
  29. tick time.Duration
  30. epm map[int8][]*experiment.Experiment
  31. c *conf.Config
  32. }
  33. func New(c *conf.Config) (s *Service) {
  34. s = &Service{
  35. c: c,
  36. dao: expdao.New(c),
  37. // tick
  38. tick: time.Duration(c.Tick),
  39. epm: map[int8][]*experiment.Experiment{},
  40. }
  41. s.loadAbTest()
  42. go s.tickproc()
  43. return
  44. }
  45. // TemporaryABTests 临时的各种abtest垃圾需求
  46. func (s *Service) TemporaryABTests(c context.Context, buvid string) (tests *experiment.ABTestV2) {
  47. id := farm.Hash32([]byte(buvid))
  48. n := int(id % 100)
  49. autoPlay := 1
  50. if n > s.c.ABTest.Range {
  51. autoPlay = 2
  52. }
  53. tests = &experiment.ABTestV2{
  54. AutoPlay: autoPlay,
  55. }
  56. return
  57. }
  58. func (s *Service) Experiment(c context.Context, plat int8, build int) (eps []*experiment.Experiment) {
  59. if es, ok := s.epm[plat]; ok {
  60. LOOP:
  61. for _, ep := range es {
  62. for _, l := range ep.Limit {
  63. if model.InvalidBuild(build, l.Build, l.Condition) {
  64. continue LOOP
  65. }
  66. }
  67. eps = append(eps, ep)
  68. }
  69. }
  70. if eps == nil {
  71. if es, ok := _defaultExperiment[plat]; ok {
  72. eps = es
  73. } else {
  74. eps = _emptyExperiment
  75. }
  76. }
  77. return
  78. }
  79. // tickproc tick load cache.
  80. func (s *Service) tickproc() {
  81. for {
  82. time.Sleep(s.tick)
  83. s.loadAbTest()
  84. }
  85. }
  86. func (s *Service) loadAbTest() {
  87. c := context.TODO()
  88. lm, err := s.dao.ExperimentLimit(c)
  89. if err != nil {
  90. log.Error("s.dao.ExperimentLimit error(%v)", err)
  91. return
  92. }
  93. ids := make([]int64, 0, len(lm))
  94. for id := range lm {
  95. ids = append(ids, id)
  96. }
  97. if len(ids) == 0 {
  98. return
  99. }
  100. eps, err := s.dao.ExperimentByIDs(c, ids)
  101. if err != nil {
  102. log.Error("s.dao.ExperimentByIDs(%v) error(%v)", ids, err)
  103. return
  104. }
  105. epm := make(map[int8][]*experiment.Experiment, len(eps))
  106. for _, ep := range eps {
  107. if l, ok := lm[ep.ID]; ok {
  108. ep.Limit = l
  109. }
  110. epm[ep.Plat] = append(epm[ep.Plat], ep)
  111. }
  112. s.epm = epm
  113. }
  114. // AbServer is
  115. func (s *Service) AbServer(c context.Context, buvid, device, mobiAPP, filteredStr string, build int, mid int64) (a interface{}, err error) {
  116. return s.dao.AbServer(c, buvid, device, mobiAPP, filteredStr, build, mid)
  117. }