diff --git a/kernel/bazaar/package.go b/kernel/bazaar/package.go index 201eaa818..30ffe9984 100644 --- a/kernel/bazaar/package.go +++ b/kernel/bazaar/package.go @@ -286,6 +286,7 @@ type StageBazaarResult struct { } var stageBazaarFlight singleflight.Group +var onlineCheckFlight singleflight.Group // getStageAndBazaar 获取 stage 索引和 bazaar 索引,相同 pkgType 的并发调用会合并为一次实际请求 (single-flight) func getStageAndBazaar(pkgType string) (result StageBazaarResult) { @@ -317,14 +318,13 @@ func getStageAndBazaar0(pkgType string) (result StageBazaarResult) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() var onlineResult bool + onlineDone := make(chan bool, 1) wg := &sync.WaitGroup{} wg.Add(3) go func() { defer wg.Done() onlineResult = isBazzarOnline() - if !onlineResult { - cancel() - } + onlineDone <- true }() go func() { defer wg.Done() @@ -334,6 +334,20 @@ func getStageAndBazaar0(pkgType string) (result StageBazaarResult) { defer wg.Done() bazaarIndex = getBazaarIndex(ctx) }() + + <-onlineDone + if !onlineResult { + // 不在线时立即取消其他请求并返回结果,避免等待 HTTP 请求超时 + cancel() + return StageBazaarResult{ + StageIndex: stageIndex, + BazaarIndex: bazaarIndex, + Online: false, + StageErr: stageErr, + } + } + + // 在线时等待所有请求完成 wg.Wait() return StageBazaarResult{ @@ -365,7 +379,7 @@ func getStageIndex(ctx context.Context, pkgType string) (ret *StageIndex, err er } var rhyRet map[string]interface{} - rhyRet, err = util.GetRhyResult(false) + rhyRet, err = util.GetRhyResult(ctx, false) if nil != err { return } @@ -494,7 +508,17 @@ func isOutdatedTemplate(template *Template, bazaarTemplates []*Template) bool { return false } -func isBazzarOnline() (ret bool) { +func isBazzarOnline() bool { + v, err, _ := onlineCheckFlight.Do("bazaarOnline", func() (interface{}, error) { + return isBazzarOnline0(), nil + }) + if err != nil { + return false + } + return v.(bool) +} + +func isBazzarOnline0() (ret bool) { // Improve marketplace loading when offline https://github.com/siyuan-note/siyuan/issues/12050 ret = util.IsOnline(util.BazaarOSSServer+"/204", true, 3000) if !ret { diff --git a/kernel/model/bazzar.go b/kernel/model/bazzar.go index 3b910f172..c873e5dc0 100644 --- a/kernel/model/bazzar.go +++ b/kernel/model/bazzar.go @@ -214,8 +214,11 @@ func BazaarPlugins(frontend, keyword string) (plugins []*bazaar.Plugin) { } func filterPlugins(plugins []*bazaar.Plugin, keyword string) (ret []*bazaar.Plugin) { - ret = []*bazaar.Plugin{} keywords := getSearchKeywords(keyword) + if 0 == len(keywords) { + return plugins + } + ret = []*bazaar.Plugin{} for _, plugin := range plugins { if matchPackage(keywords, plugin.Package) { ret = append(ret, plugin) @@ -285,8 +288,11 @@ func BazaarWidgets(keyword string) (widgets []*bazaar.Widget) { } func filterWidgets(widgets []*bazaar.Widget, keyword string) (ret []*bazaar.Widget) { - ret = []*bazaar.Widget{} keywords := getSearchKeywords(keyword) + if 0 == len(keywords) { + return widgets + } + ret = []*bazaar.Widget{} for _, w := range widgets { if matchPackage(keywords, w.Package) { ret = append(ret, w) @@ -337,8 +343,11 @@ func BazaarIcons(keyword string) (icons []*bazaar.Icon) { } func filterIcons(icons []*bazaar.Icon, keyword string) (ret []*bazaar.Icon) { - ret = []*bazaar.Icon{} keywords := getSearchKeywords(keyword) + if 0 == len(keywords) { + return icons + } + ret = []*bazaar.Icon{} for _, i := range icons { if matchPackage(keywords, i.Package) { ret = append(ret, i) @@ -400,8 +409,11 @@ func BazaarThemes(keyword string) (ret []*bazaar.Theme) { } func filterThemes(themes []*bazaar.Theme, keyword string) (ret []*bazaar.Theme) { - ret = []*bazaar.Theme{} keywords := getSearchKeywords(keyword) + if 0 == len(keywords) { + return themes + } + ret = []*bazaar.Theme{} for _, t := range themes { if matchPackage(keywords, t.Package) { ret = append(ret, t) @@ -475,8 +487,11 @@ func BazaarTemplates(keyword string) (templates []*bazaar.Template) { } func filterTemplates(templates []*bazaar.Template, keyword string) (ret []*bazaar.Template) { - ret = []*bazaar.Template{} keywords := getSearchKeywords(keyword) + if 0 == len(keywords) { + return templates + } + ret = []*bazaar.Template{} for _, t := range templates { if matchPackage(keywords, t.Package) { ret = append(ret, t) diff --git a/kernel/model/updater.go b/kernel/model/updater.go index e29a75637..4e4c32746 100644 --- a/kernel/model/updater.go +++ b/kernel/model/updater.go @@ -18,6 +18,7 @@ package model import ( "bufio" + "context" "crypto/sha256" "fmt" "io" @@ -120,7 +121,7 @@ func checkDownloadInstallPkg() { func getUpdatePkg() (downloadPkgURLs []string, checksum string, err error) { defer logging.Recover() - result, err := util.GetRhyResult(false) + result, err := util.GetRhyResult(context.TODO(), false) if err != nil { return } @@ -246,7 +247,7 @@ type Announcement struct { } func getAnnouncements() (ret []*Announcement) { - result, err := util.GetRhyResult(false) + result, err := util.GetRhyResult(context.TODO(), false) if err != nil { logging.LogErrorf("get announcement failed: %s", err) return @@ -278,7 +279,7 @@ func CheckUpdate(showMsg bool) { return } - result, err := util.GetRhyResult(showMsg) + result, err := util.GetRhyResult(context.TODO(), showMsg) if err != nil { return } diff --git a/kernel/util/rhy.go b/kernel/util/rhy.go index bca7d8b54..eb466e5ba 100644 --- a/kernel/util/rhy.go +++ b/kernel/util/rhy.go @@ -17,6 +17,7 @@ package util import ( + "context" "errors" "fmt" "sync" @@ -24,6 +25,7 @@ import ( "github.com/siyuan-note/httpclient" "github.com/siyuan-note/logging" + "golang.org/x/sync/singleflight" ) var ( @@ -32,23 +34,34 @@ var ( cachedRhyResult = map[string]interface{}{} rhyResultCacheTime int64 rhyResultLock = sync.Mutex{} + rhyResultFlight singleflight.Group ) -func GetRhyResult(force bool) (map[string]interface{}, error) { - rhyResultLock.Lock() - defer rhyResultLock.Unlock() - +func GetRhyResult(ctx context.Context, force bool) (map[string]interface{}, error) { if ContainerDocker == Container { RhyCacheDuration = int64(3600 * 24) } - now := time.Now().Unix() - if RhyCacheDuration >= now-rhyResultCacheTime && !force && 0 < len(cachedRhyResult) { + if RhyCacheDuration >= time.Now().Unix()-rhyResultCacheTime && !force && 0 < len(cachedRhyResult) { return cachedRhyResult, nil } + // 并发调用只执行一次实际请求 + v, err, _ := rhyResultFlight.Do("rhyResult", func() (interface{}, error) { + return getRhyResult0(ctx) + }) + if err != nil { + return nil, err + } + return v.(map[string]interface{}), nil +} + +func getRhyResult0(ctx context.Context) (map[string]interface{}, error) { + rhyResultLock.Lock() + defer rhyResultLock.Unlock() + request := httpclient.NewCloudRequest30s() - resp, err := request.SetSuccessResult(&cachedRhyResult).Get(GetCloudServer() + "/apis/siyuan/version?ver=" + Ver) + resp, err := request.SetContext(ctx).SetSuccessResult(&cachedRhyResult).Get(GetCloudServer() + "/apis/siyuan/version?ver=" + Ver) if err != nil { logging.LogErrorf("get version info failed: %s", err) return nil, err @@ -58,17 +71,17 @@ func GetRhyResult(force bool) (map[string]interface{}, error) { logging.LogErrorf(msg) return nil, errors.New(msg) } - rhyResultCacheTime = now + rhyResultCacheTime = time.Now().Unix() return cachedRhyResult, nil } func RefreshRhyResultJob() { - _, err := GetRhyResult(true) + _, err := GetRhyResult(context.TODO(), true) if nil != err { // 系统唤醒后可能还没有网络连接,这里等待后再重试 go func() { time.Sleep(7 * time.Second) - GetRhyResult(true) + GetRhyResult(context.TODO(), true) }() } }