mirror of
https://github.com/teivah/100-go-mistakes.git
synced 2026-06-23 01:47:08 +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 (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"net/http"
|
"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
|
type key string
|
||||||
|
|
||||||
const isValidHostKey key = "isValidHost"
|
const isValidHostKey key = "isValidHost"
|
||||||
|
|
|
||||||
|
|
@ -27,8 +27,8 @@ func listing3() {
|
||||||
s := []int{1, 2, 3}
|
s := []int{1, 2, 3}
|
||||||
|
|
||||||
for _, i := range s {
|
for _, i := range s {
|
||||||
go func(i int) {
|
go func(val int) {
|
||||||
fmt.Print(i)
|
fmt.Print(val)
|
||||||
}(i)
|
}(i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Cache struct {
|
type Cache struct {
|
||||||
mu sync.Mutex
|
mu sync.RWMutex
|
||||||
balances map[string]float64
|
balances map[string]float64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -28,9 +28,9 @@ func (c *Cache) AddBalance(id string, balance float64) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cache) AverageBalance1() float64 {
|
func (c *Cache) AverageBalance1() float64 {
|
||||||
c.mu.Lock()
|
c.mu.RLock()
|
||||||
balances := c.balances
|
balances := c.balances
|
||||||
c.mu.Unlock()
|
c.mu.RUnlock()
|
||||||
|
|
||||||
sum := 0.
|
sum := 0.
|
||||||
for _, balance := range balances {
|
for _, balance := range balances {
|
||||||
|
|
@ -40,8 +40,8 @@ func (c *Cache) AverageBalance1() float64 {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cache) AverageBalance2() float64 {
|
func (c *Cache) AverageBalance2() float64 {
|
||||||
c.mu.Lock()
|
c.mu.RLock()
|
||||||
defer c.mu.Unlock()
|
defer c.mu.RUnlock()
|
||||||
|
|
||||||
sum := 0.
|
sum := 0.
|
||||||
for _, balance := range c.balances {
|
for _, balance := range c.balances {
|
||||||
|
|
@ -51,12 +51,12 @@ func (c *Cache) AverageBalance2() float64 {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cache) AverageBalance3() float64 {
|
func (c *Cache) AverageBalance3() float64 {
|
||||||
c.mu.Lock()
|
c.mu.RLock()
|
||||||
m := make(map[string]float64, len(c.balances))
|
m := make(map[string]float64, len(c.balances))
|
||||||
for k, v := range c.balances {
|
for k, v := range c.balances {
|
||||||
m[k] = v
|
m[k] = v
|
||||||
}
|
}
|
||||||
c.mu.Unlock()
|
c.mu.RUnlock()
|
||||||
|
|
||||||
sum := 0.
|
sum := 0.
|
||||||
for _, balance := range m {
|
for _, balance := range m {
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,8 @@ import (
|
||||||
"log"
|
"log"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *conn) get1(ctx context.Context, id string) (string, int, error) {
|
func get1(ctx context.Context, db *sql.DB, id string) (string, int, error) {
|
||||||
rows, err := c.db.QueryContext(ctx,
|
rows, err := db.QueryContext(ctx,
|
||||||
"SELECT DEP, AGE FROM EMP WHERE ID = ?", id)
|
"SELECT DEP, AGE FROM EMP WHERE ID = ?", id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", 0, err
|
return "", 0, err
|
||||||
|
|
@ -33,8 +33,8 @@ func (c *conn) get1(ctx context.Context, id string) (string, int, error) {
|
||||||
return department, age, nil
|
return department, age, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *conn) get2(ctx context.Context, id string) (string, int, error) {
|
func get2(ctx context.Context, db *sql.DB, id string) (string, int, error) {
|
||||||
rows, err := c.db.QueryContext(ctx,
|
rows, err := db.QueryContext(ctx,
|
||||||
"SELECT DEP, AGE FROM EMP WHERE ID = ?", id)
|
"SELECT DEP, AGE FROM EMP WHERE ID = ?", id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", 0, err
|
return "", 0, err
|
||||||
|
|
@ -62,7 +62,3 @@ func (c *conn) get2(ctx context.Context, id string) (string, int, error) {
|
||||||
|
|
||||||
return department, age, nil
|
return department, age, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type conn struct {
|
|
||||||
db *sql.DB
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ type Event struct {
|
||||||
Data string
|
Data string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cache) TrimBefore(since time.Duration) {
|
func (c *Cache) TrimOlderThan(since time.Duration) {
|
||||||
c.mu.RLock()
|
c.mu.RLock()
|
||||||
defer c.mu.RUnlock()
|
defer c.mu.RUnlock()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCache_TrimBefore(t *testing.T) {
|
func TestCache_TrimOlderThan(t *testing.T) {
|
||||||
events := []Event{
|
events := []Event{
|
||||||
{Timestamp: time.Now().Add(-20 * time.Millisecond)},
|
{Timestamp: time.Now().Add(-20 * time.Millisecond)},
|
||||||
{Timestamp: time.Now().Add(-10 * time.Millisecond)},
|
{Timestamp: time.Now().Add(-10 * time.Millisecond)},
|
||||||
|
|
@ -13,7 +13,7 @@ func TestCache_TrimBefore(t *testing.T) {
|
||||||
}
|
}
|
||||||
cache := &Cache{}
|
cache := &Cache{}
|
||||||
cache.Add(events)
|
cache.Add(events)
|
||||||
cache.TrimBefore(15 * time.Millisecond)
|
cache.TrimOlderThan(15 * time.Millisecond)
|
||||||
got := cache.GetAll()
|
got := cache.GetAll()
|
||||||
expected := 2
|
expected := 2
|
||||||
if len(got) != expected {
|
if len(got) != expected {
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ type Event struct {
|
||||||
Data string
|
Data string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cache) TrimBefore(since time.Duration) {
|
func (c *Cache) TrimOlderThan(since time.Duration) {
|
||||||
c.mu.RLock()
|
c.mu.RLock()
|
||||||
defer c.mu.RUnlock()
|
defer c.mu.RUnlock()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCache_TrimBefore(t *testing.T) {
|
func TestCache_TrimOlderThan(t *testing.T) {
|
||||||
events := []Event{
|
events := []Event{
|
||||||
{Timestamp: parseTime(t, "2020-01-01T12:00:00.04Z")},
|
{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.05Z")},
|
||||||
|
|
@ -15,7 +15,7 @@ func TestCache_TrimBefore(t *testing.T) {
|
||||||
return parseTime(t, "2020-01-01T12:00:00.06Z")
|
return parseTime(t, "2020-01-01T12:00:00.06Z")
|
||||||
}}
|
}}
|
||||||
cache.Add(events)
|
cache.Add(events)
|
||||||
cache.TrimBefore(15 * time.Millisecond)
|
cache.TrimOlderThan(15 * time.Millisecond)
|
||||||
// ...
|
// ...
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ type Event struct {
|
||||||
Data string
|
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()
|
c.mu.RLock()
|
||||||
defer c.mu.RUnlock()
|
defer c.mu.RUnlock()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCache_TrimBefore(t *testing.T) {
|
func TestCache_TrimOlderThan(t *testing.T) {
|
||||||
events := []Event{
|
events := []Event{
|
||||||
{Timestamp: parseTime(t, "2020-01-01T12:00:00.04Z")},
|
{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.05Z")},
|
||||||
|
|
@ -13,7 +13,7 @@ func TestCache_TrimBefore(t *testing.T) {
|
||||||
}
|
}
|
||||||
cache := &Cache{}
|
cache := &Cache{}
|
||||||
cache.Add(events)
|
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()
|
got := cache.GetAll()
|
||||||
expected := 2
|
expected := 2
|
||||||
if len(got) != expected {
|
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
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func foo(r io.Reader) error {
|
func foo1(r io.Reader) error {
|
||||||
b, err := io.ReadAll(r)
|
b, err := io.ReadAll(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -23,6 +23,17 @@ func foo(r io.Reader) error {
|
||||||
return nil
|
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) {
|
func readAll(r io.Reader, retries int) ([]byte, error) {
|
||||||
b := make([]byte, 0, 512)
|
b := make([]byte, 0, 512)
|
||||||
for {
|
for {
|
||||||
|
|
@ -33,10 +44,10 @@ func readAll(r io.Reader, retries int) ([]byte, error) {
|
||||||
b = b[:len(b)+n]
|
b = b[:len(b)+n]
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
err = nil
|
return b, nil
|
||||||
}
|
}
|
||||||
retries--
|
retries--
|
||||||
if retries <= 0 {
|
if retries < 0 {
|
||||||
return b, err
|
return b, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,17 @@ func TestLowerCaseReader(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFoo(t *testing.T) {
|
func TestFoo1(t *testing.T) {
|
||||||
err := foo(iotest.TimeoutReader(
|
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)),
|
strings.NewReader(randomString(1024)),
|
||||||
))
|
))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,8 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMySQLIntegration(t *testing.T) {
|
func TestMySQLIntegration(t *testing.T) {
|
||||||
s := setupMySQL()
|
setupMySQL()
|
||||||
defer teardownMySQL(s)
|
defer teardownMySQL()
|
||||||
|
|
||||||
// ...
|
// ...
|
||||||
}
|
}
|
||||||
|
|
@ -26,12 +26,12 @@ func createConnection(t *testing.T, dsn string) *sql.DB {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
func TestMain(m *testing.M) {
|
||||||
s := setupMySQL()
|
setupMySQL()
|
||||||
code := m.Run()
|
code := m.Run()
|
||||||
teardownMySQL(s)
|
teardownMySQL()
|
||||||
os.Exit(code)
|
os.Exit(code)
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupMySQL() interface{} { return nil }
|
func setupMySQL() {}
|
||||||
|
|
||||||
func teardownMySQL(m interface{}) {}
|
func teardownMySQL() {}
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,5 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
func NewStore() Store {
|
|
||||||
return Store{}
|
|
||||||
}
|
|
||||||
|
|
||||||
type Store struct{}
|
|
||||||
|
|
||||||
func (s Store) PutCustomer(Customer) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type Customer struct {
|
type Customer struct {
|
||||||
id string
|
id string
|
||||||
}
|
}
|
||||||
|
|
||||||
func customerFactory(id string) (Customer, error) {
|
|
||||||
if id == "" {
|
|
||||||
return Customer{}, nil
|
|
||||||
}
|
|
||||||
return Customer{id: id}, nil
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,36 +1,44 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import "testing"
|
import (
|
||||||
|
"errors"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
func TestPutCustomer1(t *testing.T) {
|
func TestCustomer1(t *testing.T) {
|
||||||
customer, err := createCustomer1()
|
customer, err := createCustomer1("foo")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
err = NewStore().PutCustomer(customer)
|
// ...
|
||||||
// Check err
|
_ = customer
|
||||||
_ = err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func createCustomer1() (Customer, error) {
|
func createCustomer1(someArg string) (Customer, error) {
|
||||||
customer, err := customerFactory("foo")
|
customer, err := customerFactory(someArg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Customer{}, nil
|
return Customer{}, err
|
||||||
}
|
}
|
||||||
return customer, nil
|
return customer, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPutCustomer(t *testing.T) {
|
func TestCustomer2(t *testing.T) {
|
||||||
customer := createCustomer2(t)
|
customer := createCustomer2(t, "foo")
|
||||||
err := NewStore().PutCustomer(customer)
|
// ...
|
||||||
// Check err
|
_ = customer
|
||||||
_ = err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func createCustomer2(t *testing.T) Customer {
|
func createCustomer2(t *testing.T, someArg string) Customer {
|
||||||
customer, err := customerFactory("foo")
|
customer, err := customerFactory(someArg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
return customer
|
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