diff --git a/distributor/distributor.go b/distributor/distributor.go index 238fb25..658052f 100644 --- a/distributor/distributor.go +++ b/distributor/distributor.go @@ -254,6 +254,11 @@ func (v *VariantsDistributor) tabExperimentVariant(c *config.ProductConfig, expe if !ok { return e.Variant{}, fmt.Errorf("no layer[%s] exist in config", experiment.LayerID) } + // check 互斥域 + hitDomain := layer.HitDomain(decisionId) + if !hitDomain { + return e.Variant{}, nil + } eid, err := v.tabLayerExperimentId(layer, decisionId) if err != nil || len(eid) == 0 || eid != experiment.Id { return e.Variant{}, err diff --git a/entities/domain.go b/entities/domain.go new file mode 100644 index 0000000..b1d2b1c --- /dev/null +++ b/entities/domain.go @@ -0,0 +1,28 @@ +package entities + +import ( + "strings" + + "github.com/volcengine/datatester-go-sdk/distributor/bucketer" +) + +type Domain struct { + ID string `json:"id"` + Name string `json:"name"` + HashStrategy string `json:"hash_strategy"` + Begin uint32 `json:"begin"` + Length uint32 `json:"length"` +} + +// Hit 方法检查给定的 decisionID 是否在当前 Domain 的索引范围内 +func (d Domain) Hit(decisionID string) bool { + // 通过连接 decisionID 和 d.Name 获取流量桶索引 + index, err := bucketer.NewMmh3BucketService().GetTrafficBucketIndex( + strings.Join([]string{decisionID, d.Name}, ":")) + // 如果获取索引过程中出现错误,返回 false + if err != nil { + return false + } + // 如果索引值在 d.Begin 和 d.Begin + d.Length 之间,返回 true,否则返回 false + return index >= d.Begin && index < d.Begin+d.Length +} diff --git a/entities/layer.go b/entities/layer.go index 82290c0..352a3ed 100644 --- a/entities/layer.go +++ b/entities/layer.go @@ -14,4 +14,25 @@ type Layer struct { Name string `json:"name"` TrafficAllocation []release.TrafficAllocation `json:"traffic_allocation"` ExperimentIds []string `json:"experiment_ids"` + Domain *Domain `json:"domain"` + ParentDomains []Domain `json:"parent_domains"` +} + +// HitDomain 方法用于检查给定的 decisionID 是否命中当前层及其所有父域 +func (l Layer) HitDomain(decisionID string) bool { + // 层不属于互斥域,直接返回 + if l.Domain == nil || l.Domain.ID == "0" { + return true + } + hit := l.Domain.Hit(decisionID) + if !hit { + return false + } + for _, d := range l.ParentDomains { + hit = d.Hit(decisionID) + if !hit { + return false + } + } + return true }