diff --git a/cached_ops.go b/cached_ops.go index 4ccad1a..009e573 100644 --- a/cached_ops.go +++ b/cached_ops.go @@ -251,7 +251,9 @@ func generateArticle(key cache.Key) (cache.Cacheable, error) { r := key.(*ArticleRequest) ctx := context.TODO() ctx = context.WithValue(ctx, CtxKeyBoardname, r) - ctx = extcache.WithExtCache(ctx, extCache) + if config.Experiments.ExtCache.Enabled(fastStrHash64(r.Filename)) { + ctx = extcache.WithExtCache(ctx, extCache) + } p, err := r.Select(pttbbs.SelectHead, 0, HeadSize) if err != nil { @@ -332,7 +334,9 @@ func generateArticlePart(key cache.Key) (cache.Cacheable, error) { r := key.(*ArticlePartRequest) ctx := context.TODO() ctx = context.WithValue(ctx, CtxKeyBoardname, r) - ctx = extcache.WithExtCache(ctx, extCache) + if config.Experiments.ExtCache.Enabled(fastStrHash64(r.Filename)) { + ctx = extcache.WithExtCache(ctx, extCache) + } p, err := ptt.GetArticleSelect(r.Brd.Ref(), pttbbs.SelectHead, r.Filename, r.CacheKey, r.Offset, -1) if err == pttbbs.ErrNotFound { diff --git a/config.go b/config.go index d972b63..3f1b823 100644 --- a/config.go +++ b/config.go @@ -4,6 +4,7 @@ import ( "errors" "github.com/ptt/pttweb/captcha" + "github.com/ptt/pttweb/experiment" "github.com/ptt/pttweb/extcache" ) @@ -42,6 +43,12 @@ type PttwebConfig struct { CaptchaRedisConfig *captcha.RedisConfig ExtCacheConfig extcache.Config + + Experiments Experiments +} + +type Experiments struct { + ExtCache experiment.OptIn } const ( diff --git a/experiment/struct.go b/experiment/struct.go new file mode 100644 index 0000000..823118d --- /dev/null +++ b/experiment/struct.go @@ -0,0 +1,32 @@ +package experiment + +import ( + "encoding/json" + "fmt" +) + +const percentBase = uint64(10000) + +type Percent struct { + threshold uint64 +} + +func (p *Percent) UnmarshalJSON(b []byte) error { + var pct float64 + if err := json.Unmarshal(b, &pct); err != nil { + return err + } + if pct < 0 { + return fmt.Errorf("percent is negative: %v", pct) + } + p.threshold = uint64(pct * float64(percentBase) / 100) + return nil +} + +type OptIn struct { + OptIn Percent +} + +func (o *OptIn) Enabled(val uint64) bool { + return val%percentBase < o.OptIn.threshold +} diff --git a/pttweb.go b/pttweb.go index 0e86fb8..841371a 100644 --- a/pttweb.go +++ b/pttweb.go @@ -6,6 +6,7 @@ import ( "errors" "flag" "fmt" + "hash/fnv" "html/template" "log" "net" @@ -954,3 +955,9 @@ func manSelectType(m pttbbs.SelectMethod) manpb.ArticleRequest_SelectType { panic("unknown select type") } } + +func fastStrHash64(s string) uint64 { + h := fnv.New64() + _, _ = h.Write([]byte(s)) + return h.Sum64() +}