This commit is contained in:
teivah 2022-02-07 09:51:28 +01:00
parent ab22488bb0
commit 9e752d102c
No known key found for this signature in database
GPG key ID: 07E4A13FB8F8DF63
18 changed files with 173 additions and 70 deletions

View file

@ -0,0 +1,6 @@
package flight
type Position struct {
Lat float32
Lng float32
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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)
// ... // ...
} }

View file

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

View file

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

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

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

View file

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

View file

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

View file

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

View file

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

View file

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