首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >让singleflight.Group.Do重试

让singleflight.Group.Do重试
EN

Stack Overflow用户
提问于 2022-09-05 07:31:34
回答 1查看 72关注 0票数 0

我试着用单机缓存请求,这是开箱即用的。

我想再做一次,然后重试失败(错误的请求)以获得相同的密钥。为此,我打电话给group.Forget(Key)。但随后的调用似乎只是重用先前的结果,而不是重试。

代码语言:javascript
复制
type Result struct {
    v int
    k string
}

var group singleflight.Group

// see https://encore.dev/blog/advanced-go-concurrency
func main() {

    if true {
        for k := 0; k <= 2; k++ {
            go doGroup(context.Background(), "sameKey")
        }

        <-time.Tick(5 * time.Second)

        for k := 0; k <= 3; k++ {
            go doGroup(context.Background(), "sameKey")
        }

        <-time.Tick(30 * time.Second)
    }

}
func doGroup(ctx context.Context, key string) (*Result, error) {

    log.Println("Inside normal call")

    results, err, shared := group.Do(key, func() (interface{}, error) {
        r, e := doExpensive(ctx, key)

        // Do this; so if it encountered an error;
        // subsequent calls will retry
        // didnt work
        // perhaps because of timing
        if e != nil {
            group.Forget(key)
        }

        return r, e
    })

    fmt.Printf("Call to multiple callers: %v\n", shared)
    // does not retry if error occured

    if err != nil {
        wrapped := fmt.Errorf("error bruh %s: %w", key, err)
        fmt.Printf("%s\n", wrapped.Error())
        return nil, wrapped
    }

    fmt.Printf("Results: %v\n", results)

    return results.(*Result), err
}

func doExpensive(ctx context.Context, key string) (*Result, error) {
    log.Printf("Inside Expensive function with key %s\n", key)
    <-time.Tick(time.Second * 10)

    dice := rand.Int31n(10)

    if true {
        // <-time.Tick(time.Millisecond * time.Duration(dice*100))
        return nil, errors.New("operation failed")
    }

    <-time.Tick(time.Second * time.Duration(dice))
    return &Result{
        v: int(dice),
        k: key,
    }, nil
}

我模拟了对doGroup的调用之间的等待,因此第二个调用实际上忘记了密钥。但是doExpensive函数似乎只被调用过一次。

在这里可以找到我的代码的复制。

https://go.dev/play/p/psGjFTypU6C

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-09-05 12:07:45

这里的问题是Forget方法的时间和行为的结合。正如文档中所指出的那样

忘记告诉单飞忘记一把钥匙。以后对该键的调用将调用该函数,而不是等待更早的调用完成。

未来意味着对group.Do的所有调用都发生在对group.Forget的调用之后。在您的示例中,对group.Do的所有调用都发生在group.Forget调用之前,所有调用都得到了第一个失败调用的结果。可能的方法是在`group.Do调用之外执行触发器重试。就像这样:

代码语言:javascript
复制
package main

import (
    "context"
    "errors"
    "log"
    "math/rand"
    "sync/atomic"
    "time"

    "golang.org/x/sync/singleflight"
)

type Result struct {
    v int
    k string
}

var group singleflight.Group

func main() {
    for k := 0; k <= 2; k++ {
        go doGroup(context.Background(), "sameKey")
    }

    <-time.Tick(5 * time.Second)

    for k := 0; k <= 3; k++ {
        go doGroup(context.Background(), "sameKey")
    }

    <-time.Tick(30 * time.Second)
}

func doGroup(ctx context.Context, key string) (*Result, error) {
    log.Println("Inside normal call")

    for {
        results, err, shared := group.Do(key, func() (interface{}, error) {
            return doExpensive(ctx, key)
        })

        if err != nil {
            log.Printf("Normal call error: %s. Will retry \n", err)
            continue
        }

        log.Printf("Normal call results: %v [shared=%v]\n", results, shared)

        return results.(*Result), err
    }
}

var returnedFirstErr atomic.Bool

func doExpensive(ctx context.Context, key string) (r *Result, e error) {
    log.Printf("Inside Expensive function with key %s\n", key)
    defer func() {
        log.Printf("Result of Expensive function: [%v, %s] for %s\n", r, e, key)
    }()
    <-time.Tick(time.Second * 10)

    dice := rand.Int31n(10)

    if !returnedFirstErr.Load() {
        returnedFirstErr.Store(true)
        return nil, errors.New("operation failed")
    }

    return &Result{
        v: int(dice),
        k: key,
    }, nil
}

附带问题。你确定singleflight的行为是你所需要的吗?也许你应该用sync.Once代替?在singleflight的情况下,您可以防止多个调用同时发生,即稍后完成的调用仍然被执行。在sync.Once的情况下,调用在进程的生命周期中精确执行一次。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/73605910

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档