mirror of
https://github.com/teivah/100-go-mistakes.git
synced 2026-06-21 08:57:07 +08:00
Reviews
This commit is contained in:
parent
ab22488bb0
commit
9e752d102c
18 changed files with 173 additions and 70 deletions
6
08-concurrency-foundations/60-contexts/flight/flight.go
Normal file
6
08-concurrency-foundations/60-contexts/flight/flight.go
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
package flight
|
||||
|
||||
type Position struct {
|
||||
Lat float32
|
||||
Lng float32
|
||||
}
|
||||
|
|
@ -3,8 +3,25 @@ package main
|
|||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/teivah/100-go-mistakes/08-concurrency-foundations/60-contexts/flight"
|
||||
)
|
||||
|
||||
type publisher interface {
|
||||
Publish(ctx context.Context, position flight.Position) error
|
||||
}
|
||||
|
||||
type publishHandler struct {
|
||||
pub publisher
|
||||
}
|
||||
|
||||
func (h publishHandler) publishPosition(position flight.Position) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 4*time.Second)
|
||||
defer cancel()
|
||||
return h.pub.Publish(ctx, position)
|
||||
}
|
||||
|
||||
type key string
|
||||
|
||||
const isValidHostKey key = "isValidHost"
|
||||
|
|
|
|||
|
|
@ -27,8 +27,8 @@ func listing3() {
|
|||
s := []int{1, 2, 3}
|
||||
|
||||
for _, i := range s {
|
||||
go func(i int) {
|
||||
fmt.Print(i)
|
||||
go func(val int) {
|
||||
fmt.Print(val)
|
||||
}(i)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ func main() {
|
|||
}
|
||||
|
||||
type Cache struct {
|
||||
mu sync.Mutex
|
||||
mu sync.RWMutex
|
||||
balances map[string]float64
|
||||
}
|
||||
|
||||
|
|
@ -28,9 +28,9 @@ func (c *Cache) AddBalance(id string, balance float64) {
|
|||
}
|
||||
|
||||
func (c *Cache) AverageBalance1() float64 {
|
||||
c.mu.Lock()
|
||||
c.mu.RLock()
|
||||
balances := c.balances
|
||||
c.mu.Unlock()
|
||||
c.mu.RUnlock()
|
||||
|
||||
sum := 0.
|
||||
for _, balance := range balances {
|
||||
|
|
@ -40,8 +40,8 @@ func (c *Cache) AverageBalance1() float64 {
|
|||
}
|
||||
|
||||
func (c *Cache) AverageBalance2() float64 {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
|
||||
sum := 0.
|
||||
for _, balance := range c.balances {
|
||||
|
|
@ -51,12 +51,12 @@ func (c *Cache) AverageBalance2() float64 {
|
|||
}
|
||||
|
||||
func (c *Cache) AverageBalance3() float64 {
|
||||
c.mu.Lock()
|
||||
c.mu.RLock()
|
||||
m := make(map[string]float64, len(c.balances))
|
||||
for k, v := range c.balances {
|
||||
m[k] = v
|
||||
}
|
||||
c.mu.Unlock()
|
||||
c.mu.RUnlock()
|
||||
|
||||
sum := 0.
|
||||
for _, balance := range m {
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ import (
|
|||
"log"
|
||||
)
|
||||
|
||||
func (c *conn) get1(ctx context.Context, id string) (string, int, error) {
|
||||
rows, err := c.db.QueryContext(ctx,
|
||||
func get1(ctx context.Context, db *sql.DB, id string) (string, int, error) {
|
||||
rows, err := db.QueryContext(ctx,
|
||||
"SELECT DEP, AGE FROM EMP WHERE ID = ?", id)
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
|
|
@ -33,8 +33,8 @@ func (c *conn) get1(ctx context.Context, id string) (string, int, error) {
|
|||
return department, age, nil
|
||||
}
|
||||
|
||||
func (c *conn) get2(ctx context.Context, id string) (string, int, error) {
|
||||
rows, err := c.db.QueryContext(ctx,
|
||||
func get2(ctx context.Context, db *sql.DB, id string) (string, int, error) {
|
||||
rows, err := db.QueryContext(ctx,
|
||||
"SELECT DEP, AGE FROM EMP WHERE ID = ?", id)
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
|
|
@ -62,7 +62,3 @@ func (c *conn) get2(ctx context.Context, id string) (string, int, error) {
|
|||
|
||||
return department, age, nil
|
||||
}
|
||||
|
||||
type conn struct {
|
||||
db *sql.DB
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ type Event struct {
|
|||
Data string
|
||||
}
|
||||
|
||||
func (c *Cache) TrimBefore(since time.Duration) {
|
||||
func (c *Cache) TrimOlderThan(since time.Duration) {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
func TestCache_TrimBefore(t *testing.T) {
|
||||
func TestCache_TrimOlderThan(t *testing.T) {
|
||||
events := []Event{
|
||||
{Timestamp: time.Now().Add(-20 * time.Millisecond)},
|
||||
{Timestamp: time.Now().Add(-10 * time.Millisecond)},
|
||||
|
|
@ -13,7 +13,7 @@ func TestCache_TrimBefore(t *testing.T) {
|
|||
}
|
||||
cache := &Cache{}
|
||||
cache.Add(events)
|
||||
cache.TrimBefore(15 * time.Millisecond)
|
||||
cache.TrimOlderThan(15 * time.Millisecond)
|
||||
got := cache.GetAll()
|
||||
expected := 2
|
||||
if len(got) != expected {
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ type Event struct {
|
|||
Data string
|
||||
}
|
||||
|
||||
func (c *Cache) TrimBefore(since time.Duration) {
|
||||
func (c *Cache) TrimOlderThan(since time.Duration) {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
func TestCache_TrimBefore(t *testing.T) {
|
||||
func TestCache_TrimOlderThan(t *testing.T) {
|
||||
events := []Event{
|
||||
{Timestamp: parseTime(t, "2020-01-01T12:00:00.04Z")},
|
||||
{Timestamp: parseTime(t, "2020-01-01T12:00:00.05Z")},
|
||||
|
|
@ -15,7 +15,7 @@ func TestCache_TrimBefore(t *testing.T) {
|
|||
return parseTime(t, "2020-01-01T12:00:00.06Z")
|
||||
}}
|
||||
cache.Add(events)
|
||||
cache.TrimBefore(15 * time.Millisecond)
|
||||
cache.TrimOlderThan(15 * time.Millisecond)
|
||||
// ...
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ type Event struct {
|
|||
Data string
|
||||
}
|
||||
|
||||
func (c *Cache) TrimBefore(now time.Time, since time.Duration) {
|
||||
func (c *Cache) TrimOlderThan(now time.Time, since time.Duration) {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
func TestCache_TrimBefore(t *testing.T) {
|
||||
func TestCache_TrimOlderThan(t *testing.T) {
|
||||
events := []Event{
|
||||
{Timestamp: parseTime(t, "2020-01-01T12:00:00.04Z")},
|
||||
{Timestamp: parseTime(t, "2020-01-01T12:00:00.05Z")},
|
||||
|
|
@ -13,7 +13,7 @@ func TestCache_TrimBefore(t *testing.T) {
|
|||
}
|
||||
cache := &Cache{}
|
||||
cache.Add(events)
|
||||
cache.TrimBefore(parseTime(t, "2020-01-01T12:00:00.06Z"), 15*time.Millisecond)
|
||||
cache.TrimOlderThan(parseTime(t, "2020-01-01T12:00:00.06Z"), 15*time.Millisecond)
|
||||
got := cache.GetAll()
|
||||
expected := 2
|
||||
if len(got) != expected {
|
||||
|
|
|
|||
42
11-testing/87-time-api/listing4/main.go
Normal file
42
11-testing/87-time-api/listing4/main.go
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
package listing1
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Cache struct {
|
||||
mu sync.RWMutex
|
||||
events []Event
|
||||
}
|
||||
|
||||
type Event struct {
|
||||
Timestamp time.Time
|
||||
Data string
|
||||
}
|
||||
|
||||
func (c *Cache) TrimOlderThan(t time.Time) {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
|
||||
for i := 0; i < len(c.events); i++ {
|
||||
if c.events[i].Timestamp.After(t) {
|
||||
c.events = c.events[i:]
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Cache) Add(events []Event) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
c.events = append(c.events, events...)
|
||||
}
|
||||
|
||||
func (c *Cache) GetAll() []Event {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
|
||||
return c.events
|
||||
}
|
||||
31
11-testing/87-time-api/listing4/main_test.go
Normal file
31
11-testing/87-time-api/listing4/main_test.go
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
package listing1
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestCache_TrimOlderThan(t *testing.T) {
|
||||
events := []Event{
|
||||
{Timestamp: parseTime(t, "2020-01-01T12:00:00.04Z")},
|
||||
{Timestamp: parseTime(t, "2020-01-01T12:00:00.05Z")},
|
||||
{Timestamp: parseTime(t, "2020-01-01T12:00:00.06Z")},
|
||||
}
|
||||
cache := &Cache{}
|
||||
cache.Add(events)
|
||||
cache.TrimOlderThan(parseTime(t, "2020-01-01T12:00:00.06Z").
|
||||
Add(-15 * time.Millisecond))
|
||||
got := cache.GetAll()
|
||||
expected := 2
|
||||
if len(got) != expected {
|
||||
t.Fatalf("expected %d, got %d", expected, len(got))
|
||||
}
|
||||
}
|
||||
|
||||
func parseTime(t *testing.T, timestamp string) time.Time {
|
||||
ts, err := time.Parse(time.RFC3339, timestamp)
|
||||
if err != nil {
|
||||
t.FailNow()
|
||||
}
|
||||
return ts
|
||||
}
|
||||
|
|
@ -12,7 +12,7 @@ func (l LowerCaseReader) Read(p []byte) (int, error) {
|
|||
return 0, nil
|
||||
}
|
||||
|
||||
func foo(r io.Reader) error {
|
||||
func foo1(r io.Reader) error {
|
||||
b, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -23,6 +23,17 @@ func foo(r io.Reader) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func foo2(r io.Reader) error {
|
||||
b, err := readAll(r, 3)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// ...
|
||||
_ = b
|
||||
return nil
|
||||
}
|
||||
|
||||
func readAll(r io.Reader, retries int) ([]byte, error) {
|
||||
b := make([]byte, 0, 512)
|
||||
for {
|
||||
|
|
@ -33,10 +44,10 @@ func readAll(r io.Reader, retries int) ([]byte, error) {
|
|||
b = b[:len(b)+n]
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
err = nil
|
||||
return b, nil
|
||||
}
|
||||
retries--
|
||||
if retries <= 0 {
|
||||
if retries < 0 {
|
||||
return b, err
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,8 +16,17 @@ func TestLowerCaseReader(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestFoo(t *testing.T) {
|
||||
err := foo(iotest.TimeoutReader(
|
||||
func TestFoo1(t *testing.T) {
|
||||
err := foo1(iotest.TimeoutReader(
|
||||
strings.NewReader(randomString(1024)),
|
||||
))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFoo2(t *testing.T) {
|
||||
err := foo2(iotest.TimeoutReader(
|
||||
strings.NewReader(randomString(1024)),
|
||||
))
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ import (
|
|||
)
|
||||
|
||||
func TestMySQLIntegration(t *testing.T) {
|
||||
s := setupMySQL()
|
||||
defer teardownMySQL(s)
|
||||
setupMySQL()
|
||||
defer teardownMySQL()
|
||||
|
||||
// ...
|
||||
}
|
||||
|
|
@ -26,12 +26,12 @@ func createConnection(t *testing.T, dsn string) *sql.DB {
|
|||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
s := setupMySQL()
|
||||
setupMySQL()
|
||||
code := m.Run()
|
||||
teardownMySQL(s)
|
||||
teardownMySQL()
|
||||
os.Exit(code)
|
||||
}
|
||||
|
||||
func setupMySQL() interface{} { return nil }
|
||||
func setupMySQL() {}
|
||||
|
||||
func teardownMySQL(m interface{}) {}
|
||||
func teardownMySQL() {}
|
||||
|
|
|
|||
|
|
@ -1,22 +1,5 @@
|
|||
package main
|
||||
|
||||
func NewStore() Store {
|
||||
return Store{}
|
||||
}
|
||||
|
||||
type Store struct{}
|
||||
|
||||
func (s Store) PutCustomer(Customer) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type Customer struct {
|
||||
id string
|
||||
}
|
||||
|
||||
func customerFactory(id string) (Customer, error) {
|
||||
if id == "" {
|
||||
return Customer{}, nil
|
||||
}
|
||||
return Customer{id: id}, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,36 +1,44 @@
|
|||
package main
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPutCustomer1(t *testing.T) {
|
||||
customer, err := createCustomer1()
|
||||
func TestCustomer1(t *testing.T) {
|
||||
customer, err := createCustomer1("foo")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = NewStore().PutCustomer(customer)
|
||||
// Check err
|
||||
_ = err
|
||||
// ...
|
||||
_ = customer
|
||||
}
|
||||
|
||||
func createCustomer1() (Customer, error) {
|
||||
customer, err := customerFactory("foo")
|
||||
func createCustomer1(someArg string) (Customer, error) {
|
||||
customer, err := customerFactory(someArg)
|
||||
if err != nil {
|
||||
return Customer{}, nil
|
||||
return Customer{}, err
|
||||
}
|
||||
return customer, nil
|
||||
}
|
||||
|
||||
func TestPutCustomer(t *testing.T) {
|
||||
customer := createCustomer2(t)
|
||||
err := NewStore().PutCustomer(customer)
|
||||
// Check err
|
||||
_ = err
|
||||
func TestCustomer2(t *testing.T) {
|
||||
customer := createCustomer2(t, "foo")
|
||||
// ...
|
||||
_ = customer
|
||||
}
|
||||
|
||||
func createCustomer2(t *testing.T) Customer {
|
||||
customer, err := customerFactory("foo")
|
||||
func createCustomer2(t *testing.T, someArg string) Customer {
|
||||
customer, err := customerFactory(someArg)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return customer
|
||||
}
|
||||
|
||||
func customerFactory(someArg string) (Customer, error) {
|
||||
if someArg == "" {
|
||||
return Customer{}, errors.New("empty")
|
||||
}
|
||||
return Customer{id: someArg}, nil
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue