🎨 Improve the latency of marketplace requests when offline (#16973)

This commit is contained in:
Jeffrey Chen 2026-02-04 17:14:40 +08:00 committed by GitHub
parent 8ea0a334aa
commit abb7e9db8c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 76 additions and 23 deletions

View file

@ -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 {

View file

@ -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)

View file

@ -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
}

View file

@ -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)
}()
}
}