From 459d56a365aebf0d242f881721202676c1aefbbc Mon Sep 17 00:00:00 2001 From: opsoer <2258858408@qq.com> Date: Tue, 14 Mar 2023 16:44:48 +0800 Subject: [PATCH] fix single-flight ,and add UT description verification --- .../geecache/singleflight/singleflight.go | 27 ++--- .../singleflight/singleflight_test.go | 103 +++++++++++++++++- .../geecache/singleflight/singleflight.go | 27 ++--- .../singleflight/singleflight_test.go | 103 +++++++++++++++++- 4 files changed, 218 insertions(+), 42 deletions(-) diff --git a/gee-cache/day6-single-flight/geecache/singleflight/singleflight.go b/gee-cache/day6-single-flight/geecache/singleflight/singleflight.go index 85bd0dd..e5fbbd2 100644 --- a/gee-cache/day6-single-flight/geecache/singleflight/singleflight.go +++ b/gee-cache/day6-single-flight/geecache/singleflight/singleflight.go @@ -1,10 +1,12 @@ package singleflight -import "sync" +import ( + "sync" + "time" +) // call is an in-flight or completed Do call type call struct { - wg sync.WaitGroup val interface{} err error } @@ -22,25 +24,20 @@ type Group struct { // original to complete and receives the same results. func (g *Group) Do(key string, fn func() (interface{}, error)) (interface{}, error) { g.mu.Lock() + defer g.mu.Unlock() if g.m == nil { g.m = make(map[string]*call) } if c, ok := g.m[key]; ok { - g.mu.Unlock() - c.wg.Wait() - return c.val, c.err + return c.val, c.err //Subsequent requests get the lock and get data directly from the cache that is delayed-delete } c := new(call) - c.wg.Add(1) g.m[key] = c - g.mu.Unlock() + c.val, c.err = fn() //Call fn, make a request, make sure fn is only called once + go func() { + time.Sleep(time.Second) + delete(g.m, key) //Delay deletion for one second + }() - c.val, c.err = fn() - c.wg.Done() - - g.mu.Lock() - delete(g.m, key) - g.mu.Unlock() - - return c.val, c.err + return c.val, c.err //返回结果 } diff --git a/gee-cache/day6-single-flight/geecache/singleflight/singleflight_test.go b/gee-cache/day6-single-flight/geecache/singleflight/singleflight_test.go index 450951a..0ab487e 100644 --- a/gee-cache/day6-single-flight/geecache/singleflight/singleflight_test.go +++ b/gee-cache/day6-single-flight/geecache/singleflight/singleflight_test.go @@ -1,16 +1,107 @@ package singleflight import ( + "sync" "testing" + "time" ) -func TestDo(t *testing.T) { +// Record the fn execution times +var ExecNum = 0 + +func TestDoCase1(t *testing.T) { + var ExecNum = 0 var g Group - v, err := g.Do("key", func() (interface{}, error) { - return "bar", nil - }) + wg := sync.WaitGroup{} + wg.Add(1000) + for i := 0; i < 1000; i++ { + go func() { + v, err := g.Do("key", func() (interface{}, error) { + //If call fn once, add it once + ExecNum++ + return "bar", nil + }) + if v != "bar" || err != nil { + t.Error("Exec Do err") + } + }() + } + + //fn can be executed only one times + if ExecNum != 1 { + t.Error("singleFlight err") + } +} + +func TestDoCase2(t *testing.T) { + var ExecNum = 0 + var g Group + wg := sync.WaitGroup{} + wg.Add(1000) + + for i := 0; i < 1000; i++ { + go func() { + v, err := g.Do("key", func() (interface{}, error) { + //If call fn once, add it once + ExecNum++ + return "bar", nil + }) + if v != "bar" || err != nil { + t.Error("Exec Do err") + } + }() + } - if v != "bar" || err != nil { - t.Errorf("Do v = %v, error = %v", v, err) + time.Sleep(time.Second) + //One second later, fn will add one more + wg.Add(1000) + for i := 0; i < 1000; i++ { + go func() { + v, err := g.Do("key", func() (interface{}, error) { + //If call fn once, add it once + ExecNum++ + return "bar", nil + }) + if v != "bar" || err != nil { + t.Error("Exec Do err") + } + }() + } + + if ExecNum != 2 { + t.Error("singleFlight err") + } +} + +func TestDoCase3(t *testing.T) { + var ExecNum = 0 + var g Group + wg := sync.WaitGroup{} + wg.Add(1000) + for i := 0; i < 1000; i++ { + go func() { + v, err := g.Do("key", func() (interface{}, error) { + //If call fn once, add it once + ExecNum++ + return "bar", nil + }) + if v != "bar" || err != nil { + t.Error("Exec Do err") + } + + //key1 != key,fn will add one more + v, err = g.Do("key1", func() (interface{}, error) { + //If call fn once, add it once + ExecNum++ + return "bar1", nil + }) + if v != "bar1" || err != nil { + t.Error("Exec Do err") + } + }() + } + //fn can be executed only one times + if ExecNum != 2 { + t.Error("singleFlight err") } } diff --git a/gee-cache/day7-proto-buf/geecache/singleflight/singleflight.go b/gee-cache/day7-proto-buf/geecache/singleflight/singleflight.go index 85bd0dd..e5fbbd2 100644 --- a/gee-cache/day7-proto-buf/geecache/singleflight/singleflight.go +++ b/gee-cache/day7-proto-buf/geecache/singleflight/singleflight.go @@ -1,10 +1,12 @@ package singleflight -import "sync" +import ( + "sync" + "time" +) // call is an in-flight or completed Do call type call struct { - wg sync.WaitGroup val interface{} err error } @@ -22,25 +24,20 @@ type Group struct { // original to complete and receives the same results. func (g *Group) Do(key string, fn func() (interface{}, error)) (interface{}, error) { g.mu.Lock() + defer g.mu.Unlock() if g.m == nil { g.m = make(map[string]*call) } if c, ok := g.m[key]; ok { - g.mu.Unlock() - c.wg.Wait() - return c.val, c.err + return c.val, c.err //Subsequent requests get the lock and get data directly from the cache that is delayed-delete } c := new(call) - c.wg.Add(1) g.m[key] = c - g.mu.Unlock() + c.val, c.err = fn() //Call fn, make a request, make sure fn is only called once + go func() { + time.Sleep(time.Second) + delete(g.m, key) //Delay deletion for one second + }() - c.val, c.err = fn() - c.wg.Done() - - g.mu.Lock() - delete(g.m, key) - g.mu.Unlock() - - return c.val, c.err + return c.val, c.err //返回结果 } diff --git a/gee-cache/day7-proto-buf/geecache/singleflight/singleflight_test.go b/gee-cache/day7-proto-buf/geecache/singleflight/singleflight_test.go index 450951a..0ab487e 100644 --- a/gee-cache/day7-proto-buf/geecache/singleflight/singleflight_test.go +++ b/gee-cache/day7-proto-buf/geecache/singleflight/singleflight_test.go @@ -1,16 +1,107 @@ package singleflight import ( + "sync" "testing" + "time" ) -func TestDo(t *testing.T) { +// Record the fn execution times +var ExecNum = 0 + +func TestDoCase1(t *testing.T) { + var ExecNum = 0 var g Group - v, err := g.Do("key", func() (interface{}, error) { - return "bar", nil - }) + wg := sync.WaitGroup{} + wg.Add(1000) + for i := 0; i < 1000; i++ { + go func() { + v, err := g.Do("key", func() (interface{}, error) { + //If call fn once, add it once + ExecNum++ + return "bar", nil + }) + if v != "bar" || err != nil { + t.Error("Exec Do err") + } + }() + } + + //fn can be executed only one times + if ExecNum != 1 { + t.Error("singleFlight err") + } +} + +func TestDoCase2(t *testing.T) { + var ExecNum = 0 + var g Group + wg := sync.WaitGroup{} + wg.Add(1000) + + for i := 0; i < 1000; i++ { + go func() { + v, err := g.Do("key", func() (interface{}, error) { + //If call fn once, add it once + ExecNum++ + return "bar", nil + }) + if v != "bar" || err != nil { + t.Error("Exec Do err") + } + }() + } - if v != "bar" || err != nil { - t.Errorf("Do v = %v, error = %v", v, err) + time.Sleep(time.Second) + //One second later, fn will add one more + wg.Add(1000) + for i := 0; i < 1000; i++ { + go func() { + v, err := g.Do("key", func() (interface{}, error) { + //If call fn once, add it once + ExecNum++ + return "bar", nil + }) + if v != "bar" || err != nil { + t.Error("Exec Do err") + } + }() + } + + if ExecNum != 2 { + t.Error("singleFlight err") + } +} + +func TestDoCase3(t *testing.T) { + var ExecNum = 0 + var g Group + wg := sync.WaitGroup{} + wg.Add(1000) + for i := 0; i < 1000; i++ { + go func() { + v, err := g.Do("key", func() (interface{}, error) { + //If call fn once, add it once + ExecNum++ + return "bar", nil + }) + if v != "bar" || err != nil { + t.Error("Exec Do err") + } + + //key1 != key,fn will add one more + v, err = g.Do("key1", func() (interface{}, error) { + //If call fn once, add it once + ExecNum++ + return "bar1", nil + }) + if v != "bar1" || err != nil { + t.Error("Exec Do err") + } + }() + } + //fn can be executed only one times + if ExecNum != 2 { + t.Error("singleFlight err") } }